1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: win_dbg.c 1257 2016-09-20 17:14:21Z wesleyjohnson $
5 //
6 // Copyright (C) 1998-2016 by DooM Legacy Team.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 //
19 // $Log: win_dbg.c,v $
20 // Revision 1.8  2001/01/25 22:15:45  bpereira
21 // added heretic support
22 //
23 // Revision 1.7  2000/09/28 20:57:22  bpereira
24 // Revision 1.6  2000/09/01 19:34:38  bpereira
25 // Revision 1.5  2000/08/03 17:57:42  bpereira
26 // Revision 1.4  2000/04/24 20:24:39  bpereira
27 // Revision 1.3  2000/04/16 18:38:07  bpereira
28 // Revision 1.2  2000/02/27 00:42:12  hurdler
29 // Revision 1.1.1.1  2000/02/22 20:32:33  hurdler
30 // Initial import into CVS (v1.29 pr3)
31 //
32 //
33 // DESCRIPTION:
34 //      this source file contains the exception handler for recording error
35 //      information after crashes.
36 //      Sources from GameDeveloper magazine article, January 1998, by Bruce Dawson.
37 //
38 //-----------------------------------------------------------------------------
39 
40 // Because of WINVER redefine, doomtype.h (via doomincl.h) is before any
41 // other include that might define WINVER
42 #include "doomincl.h"
43   // VERSION
44 
45 #include "win_dbg.h"
46 #include "win_main.h"
47 
48 #include "m_argv.h" //print the parameter in the log
49 
50 #define NumCodeBytes    16          // Number of code bytes to record.
51 #define MaxStackDump    2048    // Maximum number of DWORDS in stack dumps.
52 #define StackColumns    8               // Number of columns in stack dump.
53 
54 #define ONEK                    1024
55 #define SIXTYFOURK              (64*ONEK)
56 #define ONEM                    (ONEK*ONEK)
57 #define ONEG                    (ONEK*ONEK*ONEK)
58 
59 
60 // --------------------------------------------------------------------------
61 // return a description for an ExceptionCode
62 // --------------------------------------------------------------------------
GetExceptionDescription(DWORD ExceptionCode)63 static char* GetExceptionDescription (DWORD ExceptionCode)
64 {
65     int i;
66 
67     struct ExceptionNames
68     {
69         DWORD   ExceptionCode;
70         char*   ExceptionName;
71     };
72 
73     struct ExceptionNames ExceptionMap[] =
74     {
75         {EXCEPTION_ACCESS_VIOLATION, "an Access Violation"},
76         {EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "a Array Bounds Exceeded"},
77         {EXCEPTION_BREAKPOINT, "a Breakpoint"},
78         {EXCEPTION_DATATYPE_MISALIGNMENT, "a Datatype Misalignment"},
79         {EXCEPTION_FLT_DENORMAL_OPERAND, "a Float Denormal Operand"},
80         {EXCEPTION_FLT_DIVIDE_BY_ZERO, "a Float Divide By Zero"},
81         {EXCEPTION_FLT_INEXACT_RESULT, "a Float Inexact Result"},
82         {EXCEPTION_FLT_INVALID_OPERATION, "a Float Invalid Operation"},
83         {EXCEPTION_FLT_OVERFLOW, "a Float Overflow"},
84         {EXCEPTION_FLT_STACK_CHECK, "a Float Stack Check"},
85         {EXCEPTION_FLT_UNDERFLOW, "a Float Underflow"},
86         {EXCEPTION_ILLEGAL_INSTRUCTION, "an Illegal Instruction"},
87         {EXCEPTION_IN_PAGE_ERROR, "an In Page Error"},
88         {EXCEPTION_INT_DIVIDE_BY_ZERO, "an Integer Divide By Zero"},
89         {EXCEPTION_INT_OVERFLOW, "an Integer Overflow"},
90         {EXCEPTION_INVALID_DISPOSITION, "an Invalid Disposition"},
91         {EXCEPTION_NONCONTINUABLE_EXCEPTION, "Noncontinuable Exception"},
92         {EXCEPTION_PRIV_INSTRUCTION, "a Privileged Instruction"},
93         {EXCEPTION_SINGLE_STEP, "a Single Step"},
94         {EXCEPTION_STACK_OVERFLOW, "a Stack Overflow"},
95         {0x40010005, "a Control-C"},
96         {0x40010008, "a Control-Break"},
97         {0xc0000006, "an In Page Error"},
98         {0xc0000017, "a No Memory"},
99         {0xc000001d, "an Illegal Instruction"},
100         {0xc0000025, "a Noncontinuable Exception"},
101         {0xc0000142, "a DLL Initialization Failed"},
102         {0xe06d7363, "a Microsoft C++ Exception"},
103     };
104 
105     for (i = 0; i < sizeof(ExceptionMap) / sizeof(ExceptionMap[0]); i++)
106         if (ExceptionCode == ExceptionMap[i].ExceptionCode)
107             return ExceptionMap[i].ExceptionName;
108 
109     return "Unknown exception type";
110 }
111 
112 
113 // --------------------------------------------------------------------------
114 // Directly output a formatted string to the errorlog file, using win32 funcs
115 // --------------------------------------------------------------------------
FPrintf(HANDLE fileHandle,LPCTSTR lpFmt,...)116 void FPrintf (HANDLE fileHandle, LPCTSTR lpFmt, ...)
117 {
118     char    str[1999];
119     va_list arglist;
120     DWORD   bytesWritten;
121 
122     va_start (arglist, lpFmt);
123     vsprintf (str, lpFmt, arglist);
124     va_end   (arglist);
125 
126     WriteFile (fileHandle, str, strlen(str), &bytesWritten, NULL);
127 }
128 
129 
130 // --------------------------------------------------------------------------
131 // Print the specified FILETIME to output in a human readable format,
132 // without using the C run time.
133 // --------------------------------------------------------------------------
PrintTime(char * output,FILETIME TimeToPrint)134 static void PrintTime (char *output, FILETIME TimeToPrint)
135 {
136     WORD Date, Time;
137     if (FileTimeToLocalFileTime (&TimeToPrint, &TimeToPrint) &&
138         FileTimeToDosDateTime (&TimeToPrint, &Date, &Time))
139     {
140         // What a silly way to print out the file date/time.
141         wsprintf( output, "%d/%d/%d %02d:%02d:%02d",
142             (Date / 32) & 15, Date & 31, (Date / 512) + 1980,
143             (Time / 2048), (Time / 32) & 63, (Time & 31) * 2);
144     }
145     else
146         output[0] = 0;
147 }
148 
149 
GetFilePart(char * source)150 static char* GetFilePart(char *source)
151 {
152     char *result = strrchr(source, '\\');
153     if (result)
154         result++;
155     else
156         result = source;
157     return result;
158 }
159 
160 
161 // --------------------------------------------------------------------------
162 // Print information about a code module (DLL or EXE) such as its size,
163 // location, time stamp, etc.
164 // --------------------------------------------------------------------------
ShowModuleInfo(HANDLE LogFile,HINSTANCE ModuleHandle)165 static void ShowModuleInfo(HANDLE LogFile, HINSTANCE ModuleHandle)
166 {
167     char ModName[MAX_PATH];
168     IMAGE_DOS_HEADER *DosHeader;
169     IMAGE_NT_HEADERS *NTHeader;
170     HANDLE ModuleFile;
171     char TimeBuffer[100] = "";
172     DWORD FileSize = 0;
173     __try
174     {
175         if (GetModuleFileName(ModuleHandle, ModName, sizeof(ModName)) > 0)
176         {
177             // If GetModuleFileName returns greater than zero then this must
178             // be a valid code module address. Therefore we can try to walk
179             // our way through its structures to find the link time stamp.
180             DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle;
181             if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic)
182                 return;
183             NTHeader = (IMAGE_NT_HEADERS*)((char *)DosHeader
184                 + DosHeader->e_lfanew);
185             if (IMAGE_NT_SIGNATURE != NTHeader->Signature)
186                 return;
187             // Open the code module file so that we can get its file date
188             // and size.
189             ModuleFile = CreateFile(ModName, GENERIC_READ,
190                 FILE_SHARE_READ, 0, OPEN_EXISTING,
191                 FILE_ATTRIBUTE_NORMAL, 0);
192             if (ModuleFile != INVALID_HANDLE_VALUE)
193             {
194                 FILETIME        LastWriteTime;
195                 FileSize = GetFileSize(ModuleFile, 0);
196                 if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime))
197                 {
198                     wsprintf(TimeBuffer, " - file date is ");
199                     PrintTime(TimeBuffer + lstrlen(TimeBuffer), LastWriteTime);
200                 }
201                 CloseHandle(ModuleFile);
202             }
203             FPrintf (LogFile, "%s, loaded at 0x%08x - %d bytes - %08x%s\r\n",
204                 ModName, ModuleHandle, FileSize,
205                 NTHeader->FileHeader.TimeDateStamp, TimeBuffer);
206         }
207     }
208     // Handle any exceptions by continuing from this point.
209     __except(EXCEPTION_EXECUTE_HANDLER)
210     {
211     }
212 }
213 
214 // --------------------------------------------------------------------------
215 // Scan memory looking for code modules (DLLs or EXEs). VirtualQuery is used
216 // to find all the blocks of address space that were reserved or committed,
217 // and ShowModuleInfo will display module information if they are code
218 // modules.
219 // --------------------------------------------------------------------------
RecordModuleList(HANDLE LogFile)220 static void RecordModuleList(HANDLE LogFile)
221 {
222     SYSTEM_INFO     SystemInfo;
223     size_t PageSize;
224     size_t NumPages;
225     size_t pageNum = 0;
226     void *LastAllocationBase = 0;
227 
228     FPrintf (LogFile, "\r\n"
229         "\tModule list: names, addresses, sizes, time stamps "
230         "and file times:\r\n");
231 
232     // Set NumPages to the number of pages in the 4GByte address space,
233     // while being careful to avoid overflowing ints.
234     GetSystemInfo(&SystemInfo);
235     PageSize = SystemInfo.dwPageSize;
236     NumPages = 4 * (unsigned int)(ONEG / PageSize);
237     while (pageNum < NumPages)
238     {
239         MEMORY_BASIC_INFORMATION        MemInfo;
240         if (VirtualQuery((void *)(pageNum * PageSize), &MemInfo,
241             sizeof(MemInfo)))
242         {
243             if (MemInfo.RegionSize > 0)
244             {
245                 // Adjust the page number to skip over this block of memory.
246                 pageNum += MemInfo.RegionSize / PageSize;
247                 if (MemInfo.State == MEM_COMMIT && MemInfo.AllocationBase >
248                     LastAllocationBase)
249                 {
250                     // Look for new blocks of committed memory, and try
251                     // recording their module names - this will fail
252                     // gracefully if they aren't code modules.
253                     LastAllocationBase = MemInfo.AllocationBase;
254                     ShowModuleInfo(LogFile, (HINSTANCE)LastAllocationBase);
255                 }
256             }
257             else
258                 pageNum += SIXTYFOURK / PageSize;
259         }
260         else
261             pageNum += SIXTYFOURK / PageSize;
262         // If VirtualQuery fails we advance by 64K because that is the
263         // granularity of address space doled out by VirtualAlloc().
264     }
265 }
266 
267 
268 // --------------------------------------------------------------------------
269 // Record information about the user's system, such as processor type, amount
270 // of memory, etc.
271 // --------------------------------------------------------------------------
RecordSystemInformation(HANDLE fileHandle)272 static void RecordSystemInformation(HANDLE fileHandle)
273 {
274     FILETIME        CurrentTime;
275     char        TimeBuffer[100];
276     char        ModuleName[MAX_PATH];
277     char        UserName[200];
278     DWORD       UserNameSize;
279     SYSTEM_INFO     SystemInfo;
280     MEMORYSTATUS MemInfo;
281 
282     GetSystemTimeAsFileTime (&CurrentTime);
283     PrintTime (TimeBuffer, CurrentTime);
284     FPrintf(fileHandle, "Error occurred at %s.\r\n", TimeBuffer);
285 
286     if (GetModuleFileName (NULL, ModuleName, sizeof(ModuleName)) <= 0)
287         lstrcpy (ModuleName, "Unknown");
288     UserNameSize = sizeof(UserName);
289     if (!GetUserName (UserName, &UserNameSize))
290         lstrcpy (UserName, "Unknown");
291     FPrintf(fileHandle, "%s, run by %s.\r\n", ModuleName, UserName);
292 
293     GetSystemInfo (&SystemInfo);
294     FPrintf (fileHandle, "%d processor(s), type %d %d.%d.\r\n"
295         "Program Memory from 0x%p to 0x%p\r\n",
296         SystemInfo.dwNumberOfProcessors,
297         SystemInfo.dwProcessorType,
298         SystemInfo.wProcessorLevel,
299         SystemInfo.wProcessorRevision,
300         SystemInfo.lpMinimumApplicationAddress,
301         SystemInfo.lpMaximumApplicationAddress);
302 
303     MemInfo.dwLength = sizeof(MemInfo);
304     GlobalMemoryStatus(&MemInfo);
305     // Print out the amount of physical memory, rounded up.
306     FPrintf(fileHandle, "%d MBytes physical memory.\r\n", (MemInfo.dwTotalPhys +
307         ONEM - 1) / ONEM);
308 }
309 
310 
311 // --------------------------------------------------------------------------
312 // What we do here is trivial : open a file, write out the register information
313 // from the PEXCEPTION_POINTERS structure, then return EXCEPTION_CONTINUE_SEARCH
314 // whose magic value tells Win32 to proceed with its normal error handling
315 // mechanism. This is important : an error dialog will popup if possible and
316 // the debugger will hopefully coexist peacefully with the structured exception
317 // handler.
318 // --------------------------------------------------------------------------
RecordExceptionInfo(PEXCEPTION_POINTERS data,const char * Message,LPSTR lpCmdLine)319 int __cdecl RecordExceptionInfo (PEXCEPTION_POINTERS data, const char *Message, LPSTR lpCmdLine)
320 {
321     PEXCEPTION_RECORD   Exception = data->ExceptionRecord;
322     PCONTEXT            Context = data->ContextRecord;
323     char                ModuleName[MAX_PATH];
324     char                FileName[MAX_PATH] = "Unknown";
325     char*               FilePart, *lastperiod;
326     char                CrashModulePathName[MAX_PATH];
327     char*               CrashModuleFileName = "Unknown";
328     MEMORY_BASIC_INFORMATION    MemInfo;
329     static int          BeenHere=false;
330     HANDLE              fileHandle;
331     unsigned char *     code;
332     int                 codebyte,i;
333 
334     if (BeenHere)       // Going recursive! That must mean this routine crashed!
335         return EXCEPTION_CONTINUE_SEARCH;
336     BeenHere = true;
337 
338     // Create a filename to record the error information to.
339     // Store it in the executable directory.
340     if (GetModuleFileName(NULL, ModuleName, sizeof(ModuleName)) <= 0)
341         ModuleName[0] = 0;
342     FilePart = GetFilePart(ModuleName);
343 
344     // Extract the file name portion and remove it's file extension. We'll
345     // use that name shortly.
346     lstrcpy (FileName, FilePart);
347     lastperiod = strrchr (FileName, '.');
348     if (lastperiod)
349         lastperiod[0] = 0;
350     // Replace the executable filename with our error log file name.
351     lstrcpy (FilePart, "errorlog.txt");
352     fileHandle = CreateFile (ModuleName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
353         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
354     if (fileHandle == INVALID_HANDLE_VALUE)
355     {
356         OutputDebugString ("Error creating exception report");
357         return EXCEPTION_CONTINUE_SEARCH;
358     }
359 
360     // Append to the error log.
361     SetFilePointer (fileHandle, 0, 0, FILE_END);
362 
363     // Print out some blank lines to separate this error log from any previous ones.
364     FPrintf (fileHandle, "\r\n\r\n\r\n\r\n");
365     FPrintf (fileHandle, "%s -ERROR LOG-\r\n\r\n", VERSION_BANNER);
366     FPrintf (fileHandle, "Command Line parameters: ");
367     for (i = 1;i<myargc;i++)
368         FPrintf (fileHandle, "%s ", myargv[i] );
369 
370     FPrintf (fileHandle, "\r\n");
371     // VirtualQuery can be used to get the allocation base associated with a
372     // code address, which is the same as the ModuleHandle. This can be used
373     // to get the filename of the module that the crash happened in.
374     if ( VirtualQuery ((void*)Context->Eip, &MemInfo, sizeof(MemInfo)) &&
375         GetModuleFileName ((HINSTANCE)MemInfo.AllocationBase,
376         CrashModulePathName,
377         sizeof(CrashModulePathName)) > 0)
378         CrashModuleFileName = GetFilePart(CrashModulePathName);
379 
380     // Print out the beginning of the error log in a Win95 error window
381     // compatible format.
382     FPrintf (fileHandle, "%s caused an %s in module %s at %04x:%08x.\r\n",
383         FileName, GetExceptionDescription(Exception->ExceptionCode),
384         CrashModuleFileName, Context->SegCs, Context->Eip);
385     FPrintf (fileHandle, "Exception handler called in %s.\r\n", Message);
386 
387     RecordSystemInformation (fileHandle);
388 
389     // If the exception was an access violation, print out some additional
390     // information, to the error log and the debugger.
391     if (Exception->ExceptionCode == STATUS_ACCESS_VIOLATION &&
392         Exception->NumberParameters >= 2)
393     {
394         char DebugMessage[1000];
395         const char* readwrite = "Read from";
396         if (Exception->ExceptionInformation[0])
397             readwrite = "Write to";
398         wsprintf(DebugMessage, "%s location %08x caused an access violation.\r\n",
399             readwrite, Exception->ExceptionInformation[1]);
400 #ifdef  _DEBUG
401         // The VisualC++ debugger doesn't actually tell you whether a read
402         // or a write caused the access violation, nor does it tell what
403         // address was being read or written. So I fixed that.
404         OutputDebugString("Exception handler: ");
405         OutputDebugString(DebugMessage);
406 #endif
407         FPrintf(fileHandle, "%s", DebugMessage);
408     }
409 
410     FPrintf(fileHandle, "\r\n");
411 
412     // Print out the register values in a Win95 error window compatible format.
413     if ((Context->ContextFlags & CONTEXT_FULL) == CONTEXT_FULL)
414     {
415         FPrintf (fileHandle, "Registers:\r\n");
416         FPrintf (fileHandle, "EAX=%.8lx CS=%.4x EIP=%.8lx EFLGS=%.8lx\r\n",
417             Context->Eax,Context->SegCs,Context->Eip,Context->EFlags);
418         FPrintf (fileHandle, "EBX=%.8lx SS=%.4x ESP=%.8lx EBP=%.8lx\r\n",
419             Context->Ebx,Context->SegSs,Context->Esp,Context->Ebp);
420         FPrintf (fileHandle, "ECX=%.8lx DS=%.4x ESI=%.8lx FS=%.4x\r\n",
421             Context->Ecx,Context->SegDs,Context->Esi,Context->SegFs);
422         FPrintf (fileHandle, "EDX=%.8lx ES=%.4x EDI=%.8lx GS=%.4x\r\n",
423             Context->Edx,Context->SegEs,Context->Edi,Context->SegGs);
424     }
425 
426     FPrintf (fileHandle, "Bytes at CS:EIP:\r\n");
427 
428     // Print out the bytes of code at the instruction pointer. Since the
429     // crash may have been caused by an instruction pointer that was bad,
430     // this code needs to be wrapped in an exception handler, in case there
431     // is no memory to read. If the dereferencing of code[] fails, the
432     // exception handler will print '??'.
433     code = (unsigned char*)Context->Eip;
434     for (codebyte = 0; codebyte < NumCodeBytes; codebyte++)
435     {
436         __try
437         {
438             FPrintf (fileHandle, "%02x ", code[codebyte]);
439         }
440         __except(EXCEPTION_EXECUTE_HANDLER)
441         {
442             FPrintf (fileHandle, "?? ");
443         }
444     }
445 
446     // Time to print part or all of the stack to the error log. This allows
447     // us to figure out the call stack, parameters, local variables, etc.
448     FPrintf (fileHandle, "\r\n"
449         "Stack dump:\r\n");
450     __try
451     {
452         // Esp contains the bottom of the stack, or at least the bottom of
453         // the currently used area.
454         DWORD* pStack = (DWORD *)Context->Esp;
455         DWORD* pStackTop;
456         int Count = 0;
457         char    buffer[1000] = "";
458         const int safetyzone = 50;
459         char*   nearend = buffer + sizeof(buffer) - safetyzone;
460         char*   output = buffer;
461         char *Suffix;
462         __asm
463         {
464             // Load the top (highest address) of the stack from the
465             // thread information block. It will be found there in
466             // Win9x and Windows NT.
467             mov eax, fs:[4]
468             mov pStackTop, eax
469         }
470         if (pStackTop > pStack + MaxStackDump)
471             pStackTop = pStack + MaxStackDump;
472         // Too many calls to WriteFile can take a long time, causing
473         // confusing delays when programs crash. Therefore I implemented
474         // simple buffering for the stack dumping code instead of calling
475         // FPrintf directly.
476         while (pStack + 1 <= pStackTop)
477         {
478             if ((Count % StackColumns) == 0)
479                 output += wsprintf(output, "%08x: ", pStack);
480             if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop)
481                 Suffix = "\r\n";
482             else
483                 Suffix = " ";
484             output += wsprintf(output, "%08x%s", *pStack, Suffix);
485             pStack++;
486             // Check for when the buffer is almost full, and flush it to disk.
487             if (output > nearend)
488             {
489                 FPrintf (fileHandle, "%s", buffer);
490                 buffer[0] = 0;
491                 output = buffer;
492             }
493         }
494         // Print out any final characters from the cache.
495         FPrintf (fileHandle, "%s", buffer);
496     }
497     __except(EXCEPTION_EXECUTE_HANDLER)
498     {
499         FPrintf(fileHandle, "Exception encountered during stack dump.\r\n");
500     }
501 
502     RecordModuleList (fileHandle);
503 
504     CloseHandle (fileHandle);
505 
506     // Return the magic value which tells Win32 that this handler didn't
507     // actually handle the exception - so that things will proceed as per
508     // normal.
509     //BP: should put message for end user to send this file to fix any bug
510     return EXCEPTION_CONTINUE_SEARCH;
511 }
512 
513 
514     /*
515     //
516     //FPrintf ("e-mail this file to legacy@newdoom.com , so that we can fix the problem.\r\n\r\n");
517 
518     FPrintf ("Exception handler called in %s.\r\n", Message);
519 
520     GetSystemTime (&systemTime);
521     FPrintf ("Error occured at %02d/%02d/%04d %02d:%02d:%02d.\r\n",
522              systemTime.wMonth, systemTime.wDay, systemTime.wYear,
523              systemTime.wHour, systemTime.wMinute, systemTime.wSecond);
524 
525 
526     FPrintf ("%s\r\n", filename);
527     FPrintf ("Cmd-line: %s\r\n", lpCmdLine);
528 
529     // Nested exceptions can occur, get info for each one
530 
531     nER = 1;
532     while (ER)
533     {
534         if (nER++>1)
535             FPrintf ("Exception Record %d.\r\n", nER);
536 
537         FPrintf ("application caused an %s", GetExceptionCodeStr(Exception->ExceptionCode));
538 
539         if (Context->ContextFlags & CONTEXT_CONTROL)
540             FPrintf (" at %.4x:%.8x.\r\n", Context->SegCs, Context->Eip);
541 
542         // in case of..
543         if (Context->Eip != (unsigned long)Exception->ExceptionAddress)
544             FPrintf ("Exception Address = %.8x\r\n", Exception->ExceptionAddress);
545 
546         if (Exception->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
547         {
548             FPrintf ("\r\n%s location 0x%x caused an access violation.\r\n",
549                     (Exception->ExceptionInformation[0] ? "Write to" : "Read from"),
550                      Exception->ExceptionInformation[1]);
551         }
552 
553         ER = Exception->ExceptionRecord;
554     }
555 
556 
557     if (Context->ContextFlags & CONTEXT_DEBUG_REGISTERS)
558     {
559         FPrintf ("\r\nDebug Registers:\r\n");
560         FPrintf ("Dr0=%.8x  Dr1=%.8x  Dr2=%.8x\r\n"
561                  "Dr3=%.8x  Dr6=%.8x  Dr7=%.8x\r\n",
562                  Context->Dr0, Context->Dr1, Context->Dr2,
563                  Context->Dr3, Context->Dr6, Context->Dr7);
564     }
565 
566     if (Context->ContextFlags & CONTEXT_FLOATING_POINT)
567     {
568         FPrintf ("\r\nFloating Save Area:\r\n");
569         FPrintf ("ControlWord  =%.8x  TagWord      =%.8x  ErrorSelector=%.8x  DataSelector =%.8x\r\n"
570                  "StatusWord   =%.8x  ErrorOffset  =%.8x  DataOffset   =%.8x  Cr0NpxState  =%.8x\r\n",
571                  Context->FloatSave.ControlWord, Context->FloatSave.TagWord, Context->FloatSave.ErrorSelector, Context->FloatSave.DataSelector,
572                  Context->FloatSave.StatusWord, Context->FloatSave.ErrorOffset, Context->FloatSave.DataOffset, Context->FloatSave.Cr0NpxState
573                  );
574 
575         //BYTE    RegisterArea[SIZE_OF_80387_REGISTERS];
576     }
577 
578 
579     // in case of...
580     if ((Context->ContextFlags & CONTEXT_FULL) != CONTEXT_FULL)
581     {
582         if (!(Context->ContextFlags & CONTEXT_SEGMENTS))
583             FPrintf ("Note! GS,FS,ES,DS are unspecified\r\n");
584         if (!(Context->ContextFlags & CONTEXT_INTEGER))
585             FPrintf ("Note! EDI,ESI,EBX,EDX,ECX,EAX are unspecified\r\n");
586         if (!(Context->ContextFlags & CONTEXT_CONTROL))
587             FPrintf ("Note! EBP,CS:EIP,EFlags,SS:ESP are unspecified\r\n");
588     }
589 
590     if (hwdInstance!=NULL)
591     {
592         FPrintf ("\r\nHWDDriver loaded at %.8x\r\n", hwdInstance);
593         FPrintf ("DLL function pointers:\r\n");
594         for (loadfunc = hwdFuncTable, i=0; loadfunc->fnName!=NULL; loadfunc++,i++)
595         {
596             FPrintf ("hwdFuncTable[%d]: %s loaded at %.8x\r\n", i, loadfunc->fnName,
597                                                     (unsigned long) *((void**)loadfunc->fnPointer) );
598         }
599 
600     }
601 
602     FPrintf ("\r\nBytes at CS:EIP:\r\n");
603     ucptr = (unsigned char*)Context->Eip;
604     for (i=0; i<16; i++)
605         FPrintf ("%.2x ", *ucptr++);
606 
607     FPrintf ("\r\n\r\nStack dump:\r\n");
608     ulptr = (unsigned long*)Context->Esp;
609     for (i=0; i<16; i++)
610         FPrintf ("%.8x ", *ulptr++);
611 
612     //FPrintf ("Bytes at CS:EIP:\r\n");
613     //FPrintf ("%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x ");
614 
615     for (i=0; i<16; i++)
616     {
617         FPrintf ("%x
618     }
619 */
620