StackDump.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  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. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  24. #if defined(_DEBUG) || defined(_INTERNAL) || defined(IG_DEBUG_STACKTRACE)
  25. #pragma pack(push, 8)
  26. #pragma comment(linker, "/defaultlib:Dbghelp.lib")
  27. #include "Common/StackDump.h"
  28. #include "Common/Debug.h"
  29. //*****************************************************************************
  30. // Prototypes
  31. //*****************************************************************************
  32. BOOL InitSymbolInfo(void);
  33. void UninitSymbolInfo(void);
  34. void MakeStackTrace(DWORD myeip,DWORD myesp,DWORD myebp, int skipFrames, void (*callback)(const char*));
  35. void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* linenumber, unsigned int* address);
  36. void WriteStackLine(void*address, void (*callback)(const char*));
  37. //*****************************************************************************
  38. // Mis-named globals :-)
  39. //*****************************************************************************
  40. static CONTEXT gsContext;
  41. static Bool gsInit=FALSE;
  42. BOOL (__stdcall *gsSymGetLineFromAddr)(
  43. IN HANDLE hProcess,
  44. IN DWORD dwAddr,
  45. OUT PDWORD pdwDisplacement,
  46. OUT PIMAGEHLP_LINE Line
  47. );
  48. //*****************************************************************************
  49. //*****************************************************************************
  50. void StackDumpDefaultHandler(const char*line)
  51. {
  52. DEBUG_LOG((line));
  53. }
  54. //*****************************************************************************
  55. //*****************************************************************************
  56. void StackDump(void (*callback)(const char*))
  57. {
  58. if (callback == NULL)
  59. {
  60. callback = StackDumpDefaultHandler;
  61. }
  62. InitSymbolInfo();
  63. DWORD myeip,myesp,myebp;
  64. _asm
  65. {
  66. MYEIP1:
  67. mov eax, MYEIP1
  68. mov dword ptr [myeip] , eax
  69. mov eax, esp
  70. mov dword ptr [myesp] , eax
  71. mov eax, ebp
  72. mov dword ptr [myebp] , eax
  73. }
  74. MakeStackTrace(myeip,myesp,myebp, 2, callback);
  75. }
  76. //*****************************************************************************
  77. //*****************************************************************************
  78. void StackDumpFromContext(DWORD eip,DWORD esp,DWORD ebp, void (*callback)(const char*))
  79. {
  80. if (callback == NULL)
  81. {
  82. callback = StackDumpDefaultHandler;
  83. }
  84. InitSymbolInfo();
  85. MakeStackTrace(eip,esp,ebp, 0, callback);
  86. }
  87. //*****************************************************************************
  88. //*****************************************************************************
  89. BOOL InitSymbolInfo()
  90. {
  91. if (gsInit == TRUE)
  92. return TRUE;
  93. gsInit = TRUE;
  94. atexit(UninitSymbolInfo);
  95. // See if we have the line from address function
  96. // We use GetProcAddress to stop link failures at dll loadup
  97. HINSTANCE hInstDebugHlp = GetModuleHandle("dbghelp.dll");
  98. gsSymGetLineFromAddr = (BOOL (__stdcall *)( IN HANDLE,IN DWORD,OUT PDWORD,OUT PIMAGEHLP_LINE))
  99. GetProcAddress(hInstDebugHlp , "SymGetLineFromAddr");
  100. char pathname[_MAX_PATH+1];
  101. char drive[10];
  102. char directory[_MAX_PATH+1];
  103. HANDLE process;
  104. ::SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST);
  105. process = GetCurrentProcess();
  106. //Get the apps name
  107. ::GetModuleFileName(NULL, pathname, _MAX_PATH);
  108. // turn it into a search path
  109. _splitpath(pathname, drive, directory, NULL, NULL);
  110. sprintf(pathname, "%s:\\%s", drive, directory);
  111. // append the current directory to build a search path for SymInit
  112. ::lstrcat(pathname, ";.;");
  113. if(::SymInitialize(process, pathname, FALSE))
  114. {
  115. // regenerate the name of the app
  116. ::GetModuleFileName(NULL, pathname, _MAX_PATH);
  117. if(::SymLoadModule(process, NULL, pathname, NULL, 0, 0))
  118. {
  119. //Load any other relevant modules (ie dlls) here
  120. return TRUE;
  121. }
  122. ::SymCleanup(process);
  123. }
  124. return(FALSE);
  125. }
  126. //*****************************************************************************
  127. //*****************************************************************************
  128. void UninitSymbolInfo(void)
  129. {
  130. if (gsInit == FALSE)
  131. {
  132. return;
  133. }
  134. gsInit = FALSE;
  135. ::SymCleanup(GetCurrentProcess());
  136. }
  137. //*****************************************************************************
  138. //*****************************************************************************
  139. void MakeStackTrace(DWORD myeip,DWORD myesp,DWORD myebp, int skipFrames, void (*callback)(const char*))
  140. {
  141. STACKFRAME stack_frame;
  142. BOOL b_ret = TRUE;
  143. HANDLE thread = GetCurrentThread();
  144. HANDLE process = GetCurrentProcess();
  145. memset(&gsContext, 0, sizeof(CONTEXT));
  146. gsContext.ContextFlags = CONTEXT_FULL;
  147. memset(&stack_frame, 0, sizeof(STACKFRAME));
  148. stack_frame.AddrPC.Mode = AddrModeFlat;
  149. stack_frame.AddrPC.Offset = myeip;
  150. stack_frame.AddrStack.Mode = AddrModeFlat;
  151. stack_frame.AddrStack.Offset = myesp;
  152. stack_frame.AddrFrame.Mode = AddrModeFlat;
  153. stack_frame.AddrFrame.Offset = myebp;
  154. {
  155. /*
  156. if(GetThreadContext(thread, &gsContext))
  157. {
  158. memset(&stack_frame, 0, sizeof(STACKFRAME));
  159. stack_frame.AddrPC.Mode = AddrModeFlat;
  160. stack_frame.AddrPC.Offset = gsContext.Eip;
  161. stack_frame.AddrStack.Mode = AddrModeFlat;
  162. stack_frame.AddrStack.Offset = gsContext.Esp;
  163. stack_frame.AddrFrame.Mode = AddrModeFlat;
  164. stack_frame.AddrFrame.Offset = gsContext.Ebp;
  165. */
  166. //{
  167. callback("Call Stack\n**********\n");
  168. // Skip some ?
  169. unsigned int skip = skipFrames;
  170. while (b_ret&&skip)
  171. {
  172. b_ret = StackWalk( IMAGE_FILE_MACHINE_I386,
  173. process,
  174. thread,
  175. &stack_frame,
  176. NULL, //&gsContext,
  177. NULL,
  178. SymFunctionTableAccess,
  179. SymGetModuleBase,
  180. NULL);
  181. skip--;
  182. }
  183. skip = 30;
  184. while(b_ret&&skip)
  185. {
  186. b_ret = StackWalk( IMAGE_FILE_MACHINE_I386,
  187. process,
  188. thread,
  189. &stack_frame,
  190. NULL, //&gsContext,
  191. NULL,
  192. SymFunctionTableAccess,
  193. SymGetModuleBase,
  194. NULL);
  195. if (b_ret) WriteStackLine((void *) stack_frame.AddrPC.Offset, callback);
  196. skip--;
  197. }
  198. }
  199. }
  200. //*****************************************************************************
  201. //*****************************************************************************
  202. void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* linenumber, unsigned int* address)
  203. {
  204. InitSymbolInfo();
  205. if (name)
  206. {
  207. strcpy(name, "<Unknown>");
  208. }
  209. if (filename)
  210. {
  211. strcpy(filename, "<Unknown>");
  212. }
  213. if (linenumber)
  214. {
  215. *linenumber = 0xFFFFFFFF;
  216. }
  217. if (address)
  218. {
  219. *address = 0xFFFFFFFF;
  220. }
  221. ULONG displacement = 0;
  222. HANDLE process = ::GetCurrentProcess();
  223. char symbol_buffer[512 + sizeof(IMAGEHLP_SYMBOL)];
  224. memset(symbol_buffer, 0, sizeof(symbol_buffer));
  225. PIMAGEHLP_SYMBOL psymbol = (PIMAGEHLP_SYMBOL)symbol_buffer;
  226. psymbol->SizeOfStruct = sizeof(symbol_buffer);
  227. psymbol->MaxNameLength = 512;
  228. if (SymGetSymFromAddr(process, (DWORD) pointer, &displacement, psymbol))
  229. {
  230. if (name)
  231. {
  232. strcpy(name, psymbol->Name);
  233. strcat(name, "();");
  234. }
  235. // Get line now
  236. if (gsSymGetLineFromAddr)
  237. {
  238. // Unsupported for win95/98 at least with my current dbghelp.dll
  239. IMAGEHLP_LINE line;
  240. memset(&line,0,sizeof(line));
  241. line.SizeOfStruct = sizeof(line);
  242. if (gsSymGetLineFromAddr(process, (DWORD) pointer, &displacement, &line))
  243. {
  244. if (filename)
  245. {
  246. strcpy(filename, line.FileName);
  247. }
  248. if (linenumber)
  249. {
  250. *linenumber = (unsigned int)line.LineNumber;
  251. }
  252. if (address)
  253. {
  254. *address = (unsigned int)line.Address;
  255. }
  256. }
  257. }
  258. }
  259. }
  260. //*****************************************************************************
  261. // Gets last x addresses from the stack
  262. //*****************************************************************************
  263. void FillStackAddresses(void**addresses, unsigned int count, unsigned int skip)
  264. {
  265. InitSymbolInfo();
  266. STACKFRAME stack_frame;
  267. HANDLE thread = GetCurrentThread();
  268. HANDLE process = GetCurrentProcess();
  269. memset(&gsContext, 0, sizeof(CONTEXT));
  270. gsContext.ContextFlags = CONTEXT_FULL;
  271. DWORD myeip,myesp,myebp;
  272. _asm
  273. {
  274. MYEIP2:
  275. mov eax, MYEIP2
  276. mov dword ptr [myeip] , eax
  277. mov eax, esp
  278. mov dword ptr [myesp] , eax
  279. mov eax, ebp
  280. mov dword ptr [myebp] , eax
  281. xor eax,eax
  282. }
  283. memset(&stack_frame, 0, sizeof(STACKFRAME));
  284. stack_frame.AddrPC.Mode = AddrModeFlat;
  285. stack_frame.AddrPC.Offset = myeip;
  286. stack_frame.AddrStack.Mode = AddrModeFlat;
  287. stack_frame.AddrStack.Offset = myesp;
  288. stack_frame.AddrFrame.Mode = AddrModeFlat;
  289. stack_frame.AddrFrame.Offset = myebp;
  290. {
  291. /*
  292. if(GetThreadContext(thread, &gsContext))
  293. {
  294. memset(&stack_frame, 0, sizeof(STACKFRAME));
  295. stack_frame.AddrPC.Mode = AddrModeFlat;
  296. stack_frame.AddrPC.Offset = gsContext.Eip;
  297. stack_frame.AddrStack.Mode = AddrModeFlat;
  298. stack_frame.AddrStack.Offset = gsContext.Esp;
  299. stack_frame.AddrFrame.Mode = AddrModeFlat;
  300. stack_frame.AddrFrame.Offset = gsContext.Ebp;
  301. */
  302. Bool stillgoing = TRUE;
  303. // unsigned int cd = count;
  304. // Skip some?
  305. while (stillgoing&&skip)
  306. {
  307. stillgoing = StackWalk(IMAGE_FILE_MACHINE_I386,
  308. process,
  309. thread,
  310. &stack_frame,
  311. NULL, //&gsContext,
  312. NULL,
  313. SymFunctionTableAccess,
  314. SymGetModuleBase,
  315. NULL) != 0;
  316. skip--;
  317. }
  318. while(stillgoing&&count)
  319. {
  320. stillgoing = StackWalk(IMAGE_FILE_MACHINE_I386,
  321. process,
  322. thread,
  323. &stack_frame,
  324. NULL, //&gsContext,
  325. NULL,
  326. SymFunctionTableAccess,
  327. SymGetModuleBase,
  328. NULL) != 0;
  329. if (stillgoing)
  330. {
  331. *addresses = (void*)stack_frame.AddrPC.Offset;
  332. addresses++;
  333. count--;
  334. }
  335. }
  336. // Fill remainder
  337. while (count)
  338. {
  339. *addresses = NULL;
  340. addresses++;
  341. count--;
  342. }
  343. }
  344. /*
  345. else
  346. {
  347. memset(addresses,NULL,count*sizeof(void*));
  348. }
  349. */
  350. }
  351. //*****************************************************************************
  352. // Do full stack dump using an address array
  353. //*****************************************************************************
  354. void StackDumpFromAddresses(void**addresses, unsigned int count, void (*callback)(const char *))
  355. {
  356. if (callback == NULL)
  357. {
  358. callback = StackDumpDefaultHandler;
  359. }
  360. InitSymbolInfo();
  361. while ((count--) && (*addresses!=NULL))
  362. {
  363. WriteStackLine(*addresses,callback);
  364. addresses++;
  365. }
  366. }
  367. AsciiString g_LastErrorDump;
  368. //*****************************************************************************
  369. //*****************************************************************************
  370. void WriteStackLine(void*address, void (*callback)(const char*))
  371. {
  372. static char line[MAX_PATH];
  373. static char function_name[512];
  374. static char filename[MAX_PATH];
  375. unsigned int linenumber;
  376. unsigned int addr;
  377. GetFunctionDetails(address, function_name, filename, &linenumber, &addr);
  378. sprintf(line, " %s(%d) : %s 0x%08p", filename, linenumber, function_name, address);
  379. if (g_LastErrorDump.isNotEmpty()) {
  380. g_LastErrorDump.concat(line);
  381. g_LastErrorDump.concat("\n");
  382. }
  383. callback(line);
  384. callback("\n");
  385. }
  386. //*****************************************************************************
  387. //*****************************************************************************
  388. void DumpExceptionInfo( unsigned int u, EXCEPTION_POINTERS* e_info )
  389. {
  390. DEBUG_LOG(( "\n********** EXCEPTION DUMP ****************\n" ));
  391. /*
  392. ** List of possible exceptions
  393. */
  394. g_LastErrorDump.clear();
  395. static const unsigned int _codes[] = {
  396. EXCEPTION_ACCESS_VIOLATION,
  397. EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
  398. EXCEPTION_BREAKPOINT,
  399. EXCEPTION_DATATYPE_MISALIGNMENT,
  400. EXCEPTION_FLT_DENORMAL_OPERAND,
  401. EXCEPTION_FLT_DIVIDE_BY_ZERO,
  402. EXCEPTION_FLT_INEXACT_RESULT,
  403. EXCEPTION_FLT_INVALID_OPERATION,
  404. EXCEPTION_FLT_OVERFLOW,
  405. EXCEPTION_FLT_STACK_CHECK,
  406. EXCEPTION_FLT_UNDERFLOW,
  407. EXCEPTION_ILLEGAL_INSTRUCTION,
  408. EXCEPTION_IN_PAGE_ERROR,
  409. EXCEPTION_INT_DIVIDE_BY_ZERO,
  410. EXCEPTION_INT_OVERFLOW,
  411. EXCEPTION_INVALID_DISPOSITION,
  412. EXCEPTION_NONCONTINUABLE_EXCEPTION,
  413. EXCEPTION_PRIV_INSTRUCTION,
  414. EXCEPTION_SINGLE_STEP,
  415. EXCEPTION_STACK_OVERFLOW,
  416. 0xffffffff
  417. };
  418. /*
  419. ** Information about each exception type.
  420. */
  421. static char const * _code_txt[] = {
  422. "Error code: EXCEPTION_ACCESS_VIOLATION\nDescription: The thread tried to read from or write to a virtual address for which it does not have the appropriate access.",
  423. "Error code: EXCEPTION_ARRAY_BOUNDS_EXCEEDED\nDescription: The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking.",
  424. "Error code: EXCEPTION_BREAKPOINT\nDescription: A breakpoint was encountered.",
  425. "Error code: EXCEPTION_DATATYPE_MISALIGNMENT\nDescription: The thread tried to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries; 32-bit values on 4-byte boundaries, and so on.",
  426. "Error code: EXCEPTION_FLT_DENORMAL_OPERAND\nDescription: One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.",
  427. "Error code: EXCEPTION_FLT_DIVIDE_BY_ZERO\nDescription: The thread tried to divide a floating-point value by a floating-point divisor of zero.",
  428. "Error code: EXCEPTION_FLT_INEXACT_RESULT\nDescription: The result of a floating-point operation cannot be represented exactly as a decimal fraction.",
  429. "Error code: EXCEPTION_FLT_INVALID_OPERATION\nDescription: Some strange unknown floating point operation was attempted.",
  430. "Error code: EXCEPTION_FLT_OVERFLOW\nDescription: The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.",
  431. "Error code: EXCEPTION_FLT_STACK_CHECK\nDescription: The stack overflowed or underflowed as the result of a floating-point operation.",
  432. "Error code: EXCEPTION_FLT_UNDERFLOW\nDescription: The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.",
  433. "Error code: EXCEPTION_ILLEGAL_INSTRUCTION\nDescription: The thread tried to execute an invalid instruction.",
  434. "Error code: EXCEPTION_IN_PAGE_ERROR\nDescription: The thread tried to access a page that was not present, and the system was unable to load the page. For example, this exception might occur if a network connection is lost while running a program over the network.",
  435. "Error code: EXCEPTION_INT_DIVIDE_BY_ZERO\nDescription: The thread tried to divide an integer value by an integer divisor of zero.",
  436. "Error code: EXCEPTION_INT_OVERFLOW\nDescription: The result of an integer operation caused a carry out of the most significant bit of the result.",
  437. "Error code: EXCEPTION_INVALID_DISPOSITION\nDescription: An exception handler returned an invalid disposition to the exception dispatcher. Programmers using a high-level language such as C should never encounter this exception.",
  438. "Error code: EXCEPTION_NONCONTINUABLE_EXCEPTION\nDescription: The thread tried to continue execution after a noncontinuable exception occurred.",
  439. "Error code: EXCEPTION_PRIV_INSTRUCTION\nDescription: The thread tried to execute an instruction whose operation is not allowed in the current machine mode.",
  440. "Error code: EXCEPTION_SINGLE_STEP\nDescription: A trace trap or other single-instruction mechanism signaled that one instruction has been executed.",
  441. "Error code: EXCEPTION_STACK_OVERFLOW\nDescription: The thread used up its stack.",
  442. "Error code: ?????\nDescription: Unknown exception."
  443. };
  444. DEBUG_LOG( ("Dump exception info\n") );
  445. CONTEXT *context = e_info->ContextRecord;
  446. /*
  447. ** The following are set for access violation only
  448. */
  449. int access_read_write=-1;
  450. unsigned long access_address = 0;
  451. AsciiString msg;
  452. // DOUBLE_DEBUG does a DEBUG_LOG, and concats to g_LastErrorDump. jba.
  453. #define DOUBLE_DEBUG(x) { msg.format x; g_LastErrorDump.concat(msg); DEBUG_LOG( x ); }
  454. if ( e_info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION )
  455. {
  456. DOUBLE_DEBUG (("Exception is access violation\n"));
  457. access_read_write = e_info->ExceptionRecord->ExceptionInformation[0]; // 0=read, 1=write
  458. access_address = e_info->ExceptionRecord->ExceptionInformation[1];
  459. }
  460. else
  461. {
  462. DOUBLE_DEBUG (("Exception code is %x\n", e_info->ExceptionRecord->ExceptionCode));
  463. }
  464. Int *winMainAddr = (Int *)WinMain;
  465. DOUBLE_DEBUG(("WinMain at %x\n", winMainAddr));
  466. /*
  467. ** Match the exception type with the error string and print it out
  468. */
  469. for ( int i=0 ; _codes[i] != 0xffffffff ; i++ )
  470. {
  471. if ( _codes[i] == e_info->ExceptionRecord->ExceptionCode )
  472. {
  473. DEBUG_LOG ( ("Found exception description\n") );
  474. break;
  475. }
  476. }
  477. DOUBLE_DEBUG( ("%s\n", _code_txt[i]));
  478. /** For access violations, print out the violation address and if it was read or write.
  479. */
  480. if ( e_info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION )
  481. {
  482. if ( access_read_write )
  483. {
  484. DOUBLE_DEBUG( ("Access address:%08X was written to.\n", access_address));
  485. }
  486. else
  487. {
  488. DOUBLE_DEBUG( ("Access address:%08X was read from.\n", access_address));
  489. }
  490. }
  491. DOUBLE_DEBUG (("\nStack Dump:\n"));
  492. StackDumpFromContext(context->Eip, context->Esp, context->Ebp, NULL);
  493. DOUBLE_DEBUG (("\nDetails:\n"));
  494. DOUBLE_DEBUG (("Register dump...\n"));
  495. /*
  496. ** Dump the registers.
  497. */
  498. DOUBLE_DEBUG ( ( "Eip:%08X\tEsp:%08X\tEbp:%08X\n", context->Eip, context->Esp, context->Ebp));
  499. DOUBLE_DEBUG ( ( "Eax:%08X\tEbx:%08X\tEcx:%08X\n", context->Eax, context->Ebx, context->Ecx));
  500. DOUBLE_DEBUG ( ( "Edx:%08X\tEsi:%08X\tEdi:%08X\n", context->Edx, context->Esi, context->Edi));
  501. DOUBLE_DEBUG ( ( "EFlags:%08X \n", context->EFlags));
  502. DOUBLE_DEBUG ( ( "CS:%04x SS:%04x DS:%04x ES:%04x FS:%04x GS:%04x\n", context->SegCs, context->SegSs, context->SegDs, context->SegEs, context->SegFs, context->SegGs));
  503. /*
  504. ** Dump the bytes at EIP. This will make it easier to match the crash address with later versions of the game.
  505. */
  506. char scrap[512];
  507. DOUBLE_DEBUG ( ("EIP bytes dump...\n"));
  508. wsprintf (scrap, "\nBytes at CS:EIP (%08X) : ", context->Eip);
  509. unsigned char *eip_ptr = (unsigned char *) (context->Eip);
  510. char bytestr[32];
  511. for (int c = 0 ; c < 32 ; c++)
  512. {
  513. if (IsBadReadPtr(eip_ptr, 1))
  514. {
  515. lstrcat (scrap, "?? ");
  516. }
  517. else
  518. {
  519. sprintf (bytestr, "%02X ", *eip_ptr);
  520. strcat (scrap, bytestr);
  521. }
  522. eip_ptr++;
  523. }
  524. strcat (scrap, "\n");
  525. DOUBLE_DEBUG ( ( (scrap)));
  526. DEBUG_LOG(( "********** END EXCEPTION DUMP ****************\n\n" ));
  527. }
  528. #pragma pack(pop)
  529. #endif