/*
*@@sourcefile except.c:
* this file contains powerful exception handlers.
* except.h also defines easy-to-use macros for them.
*
* Usage: All OS/2 programs, PM or text mode.
*
* Introduction
*
* OS/2 exception handlers are a mess to program and,
* if installed wrongly, almost impossible to debug.
* The problem is that for any program that does a bit
* more than showing a message box, using exception
* handlers is a must to avoid system hangs. This
* especially applies to multi-thread programs using
* mutex semaphores (more on that below). The functions
* and macros in here are designed to make that more
* simple.
*
* The macros in except.h automatically insert code for
* properly registering and deregistering the handlers
* in except.c. You should ALWAYS use these macros
* instead of directly registering the handlers to avoid
* accidentally forgetting to deregister them. If you
* forget to deregister an exception handler, this can
* lead to really strange errors (crashes, hangs) which
* are nearly impossible to debug because the thread's
* stack probably got completely messed up.
*
* The general idea of these macros is to define
* TRY / CATCH blocks similar to C++. If an exception
* occurs in the TRY block, execution is transferred to
* the CATCH block. (This works in both C and C++, by the
* way.)
*
* The "OnKill" function that was added with V0.9.0 has
* been removed again with V0.9.7.
*
* The general usage is like this:
*
+ int your_protected_func(int ...)
+ {
+ TRY_LOUD(excptid) // or: TRY_QUIET(excptid)
+ {
+ char *p = NULL;
+
+ .... // the stuff in here is protected by
+ // the excHandlerLoud or excHandlerQuiet
+ // exception handler
+ *p = "A";
+ }
+ CATCH(excptid)
+ {
+ .... // exception occured: react here
+ } END_CATCH(); // always needed!
+ } // end of your_func
*
* TRY_LOUD is for installing excHandlerLoud.
* TRY_QUIET is for installing excHandlerQuiet.
* CATCH / END_CATCH are the same for the two. This
* is where the exception handler jumps to if an
* exception occurs.
* The CATCH block is _required_ even if you do nothing
* in there, because the CATCH() macro will deregister
* the handler.
*
* "excptid" can be any C identifier which is not used in
* your current variable scope, e.g. "excpt1". This
* is used for creating an EXCEPTSTRUCT variable of
* that name on the stack. The "excptid"'s in TRY_* and
* CATCH must match, since this is where the macros
* store the exception handler data.
*
* These macros may be nested if you use different
* "excptid"'s for sub-macros.
*
* Inside the TRY and CATCH blocks, you must not use
* "goto" (to a location outside the block) or "return",
* because this will not deregister the handler.
*
* Keep in mind that all the code in the TRY_* block is
* protected by the handler, including all functions that
* get called. So if you enclose your main() code in a
* TRY_* block, your entire application is protected.
* If any subfunction fails, execution is transferred to
* the closest CATCH() that was installed (as with C++
* try and catch).
*
* Asynchronous exceptions
*
* The exception handlers in this file (which are installed
* with the TRY/CATCH mechanism) only intercept synchronous
* exceptions, most importantly, XCPT_ACCESS_VIOLATION (see
* excHandlerLoud for a list). They do not protect your code
* against asynchronous exceptions.
*
* OS/2 defines asynchronous exceptions to be those that
* can be delayed. With OS/2, there are only three of these:
*
* -- XCPT_PROCESS_TERMINATE
* -- XCPT_ASYNC_PROCESS_TERMINATE
* -- XCPT_SIGNAL (thread 1 only)
*
* To protect yourself against these also, put the section
* in question in a DosEnterMustComplete/DosExitMustComplete
* block as well.
*
* Mutex semaphores
*
* The problem with OS/2 mutex semaphores is that they are
* sometimes not automatically released when a thread terminates.
* If there are several mutexes involved and they are released
* in improper order, you can get zombie threads on exit.
* Even worse, if this happens to a PM thread, this will hang
* the system.
*
* As a result, you should protect any section of code which
* requests a semaphore with the exception handlers.
*
* So _whenever_ you request a mutex semaphore, enclose
* the block with TRY/CATCH in case the code crashes.
* Besides, enclose the TRY/CATCH block in a must-complete
* section, like this:
*
+ HMTX hmtx = ...
+
+ int your_func(int)
+ {
+ BOOL fSemOwned = FALSE;
+
+ TRY_QUIET(excpt1) // or TRY_LOUD
+ {
+ if (fSemOwned = !DosRequestMutexSem(hmtx, ...))
+ { ... // work on your protected data
+ }
+ // mutex gets released below
+ }
+ CATCH(excpt1) { } END_CATCH(); // always needed!
+
+ if (fSemOwned)
+ // this gets executed always, even if an exception occured
+ DosReleaseMutexSem(hmtx);
+ } // end of your_func
*
* This way your mutex semaphore gets released in every
* possible condition.
*
* Customizing
*
* As opposed to versions before 0.9.0, this code is now
* completely independent of XWorkplace. This file now
* contains "pure" exception handlers only.
*
* However, you can customize these exception handlers by
* calling excRegisterHooks. This is what XWorkplace does now.
* This should be done upon initialization of your application.
* If excRegisterHooks is not called, the following safe
* defaults are used:
*
* -- the trap log file is TRAP.LOG in the root
* directory of your boot drive.
*
* For details on the provided exception handlers, refer
* to excHandlerLoud and excHandlerQuiet.
*
* More useful debug information can be found in the "OS/2 Debugging
* Handbook", which is now available in INF format on the IBM
* DevCon site ("http://service2.boulder.ibm.com/devcon/").
* This book shows worked examples of how to unwind a stack dump.
*
* This file incorporates code from the following:
* -- Monte Copeland, IBM Boca Ration, Florida, USA (1993)
* -- Roman Stangl, from the Program Commander/2 sources
* (1997-98)
* -- Marc Fiammante, John Currier, Kim Rasmussen,
* Anthony Cruise (EXCEPT3.ZIP package for a generic
* exception handling DLL, available at Hobbes).
*
* If not explicitly stated otherwise, the code has been written
* by me, Ulrich M”ller.
*
* Note: Version numbering in this file relates to XWorkplace version
* numbering.
*
*@@header "helpers\except.h"
*/
/*
* This file Copyright (C) 1992-99 Ulrich M”ller,
* Monte Copeland,
* Roman Stangl,
* Kim Rasmussen,
* Marc Fiammante,
* John Currier,
* Anthony Cruise.
* This file is part of the "XWorkplace helpers" source package.
* This 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, in version 2 as it comes in the
* "COPYING" file of the XWorkplace main distribution.
* 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.
*/
#define OS2EMX_PLAIN_CHAR
// this is needed for "os2emx.h"; if this is defined,
// emx will define PSZ as _signed_ char, otherwise
// as unsigned char
#define INCL_DOSMODULEMGR
#define INCL_DOSEXCEPTIONS
#define INCL_DOSPROCESS
#define INCL_DOSMISC
#define INCL_DOSERRORS
#include
// C library headers
#include // needed for except.h
#include
#include
#include
#include // needed for except.h
#include // needed for except.h
#define DONT_REPLACE_MALLOC
#include "helpers\setup.h" // code generation and debugging options
// headers in /helpers
#include "helpers\dosh.h" // Control Program helper routines
#include "helpers\except.h" // exception handling
#include "helpers\debug.h" // symbol/debug code analysis
#pragma hdrstop
/* ******************************************************************
*
* Global variables
*
********************************************************************/
// hooks to be registered using excRegisterHooks
PFNEXCOPENFILE G_pfnExcOpenFile = 0;
PFNEXCHOOK G_pfnExcHook = 0;
PFNEXCHOOKERROR G_pfnExcHookError = 0;
// beep flag for excHandlerLoud
BOOL G_fBeepOnException = TRUE;
ULONG G_ulExplainExceptionRunning = 0;
// global flag which is != 0 if some exception handler
// is inside excExplainException, so that XShutdown can
// wait until the trap log is done;
// this is exported thru except.h
// V0.9.13 (2001-06-19) [umoeller]
/*
*@@category: Helpers\Control program helpers\Exceptions/debugging
* See except.c.
*/
/* ******************************************************************
*
* Exception helper routines
*
********************************************************************/
/*
*@@ excDescribePage:
*
*/
VOID excDescribePage(FILE *file, ULONG ulCheck)
{
APIRET arc;
ULONG ulCountPages = 1;
ULONG ulFlagsPage = 0;
arc = DosQueryMem((PVOID)ulCheck, &ulCountPages, &ulFlagsPage);
if (arc == NO_ERROR)
{
fprintf(file, "valid, flags: ");
if (ulFlagsPage & PAG_READ)
fprintf(file, "read ");
if (ulFlagsPage & PAG_WRITE)
fprintf(file, "write ");
if (ulFlagsPage & PAG_EXECUTE)
fprintf(file, "execute ");
if (ulFlagsPage & PAG_GUARD)
fprintf(file, "guard ");
if (ulFlagsPage & PAG_COMMIT)
fprintf(file, "committed ");
if (ulFlagsPage & PAG_SHARED)
fprintf(file, "shared ");
if (ulFlagsPage & PAG_FREE)
fprintf(file, "free ");
if (ulFlagsPage & PAG_BASE)
fprintf(file, "base ");
}
else if (arc == ERROR_INVALID_ADDRESS)
fprintf(file, "invalid");
}
/*
*@@ excPrintStackFrame:
* wrapper for dbgPrintStackFrame to format
* output stuff right.
*
*@@added V0.9.2 (2000-03-10) [umoeller]
*@@changed V0.9.12 (2001-05-12) [umoeller]: added seg:ofs to output always
*/
VOID excPrintStackFrame(FILE *file, // in: output log file
PSZ pszDescription, // in: description for stack frame (should be eight chars)
ULONG ulAddress) // in: address to debug
{
APIRET arc = NO_ERROR;
HMODULE hmod1 = NULLHANDLE;
CHAR szMod1[2*CCHMAXPATH] = "unknown";
ULONG ulObject = 0,
ulOffset = 0;
fprintf(file,
" %-8s: %08lX ",
pszDescription,
ulAddress);
arc = DosQueryModFromEIP(&hmod1,
&ulObject,
sizeof(szMod1), szMod1,
&ulOffset,
ulAddress);
if (arc != NO_ERROR)
{
// error:
fprintf(file,
" %-8s Error: DosQueryModFromEIP returned %lu\n",
szMod1,
arc);
}
else
{
CHAR szFullName[2*CCHMAXPATH];
fprintf(file,
" %-8s %02lX:%08lX\n ",
szMod1,
ulObject + 1, // V0.9.12 (2001-05-12) [umoeller]
ulOffset); // V0.9.12 (2001-05-12) [umoeller]
DosQueryModuleName(hmod1, sizeof(szFullName), szFullName);
dbgPrintStackFrame(file,
szFullName,
ulObject,
ulOffset);
fprintf(file, "\n");
// make a 'tick' sound to let the user know we're still alive
DosBeep(2000, 10);
}
}
/*
*@@ excDumpStackFrames:
* called from excExplainException to dump the
* thread's stack frames. This calls excPrintStackFrame
* for each stack frame found.
*
*@@added V0.9.4 (2000-06-15) [umoeller]
*/
VOID excDumpStackFrames(FILE *file, // in: logfile from fopen()
PTIB ptib,
PCONTEXTRECORD pContextRec) // in: excpt info
{
PULONG pulStackWord = 0;
fprintf(file, "\n\nStack frames:\n Address Module seg:ofs\n");
// first the trapping address itself
excPrintStackFrame(file,
"CS:EIP ",
pContextRec->ctx_RegEip);
pulStackWord = (PULONG)pContextRec->ctx_RegEbp;
/* if (pContextRec->ctx_RegEbp < pContextRec->ctx_RegEsp)
pulStackWord = (PULONG)(pContextRec->ctx_RegEbp & 0xFFFFFFF0);
else
pulStackWord = (PULONG)(pContextRec->ctx_RegEsp & 0xFFFFFFF0); */
while ( (pulStackWord != 0)
&& (pulStackWord < (PULONG)ptib->tib_pstacklimit)
)
{
CHAR szAddress[20];
if (((ULONG)pulStackWord & 0x00000FFF) == 0x00000000)
{
// we're on a page boundary: check access
ULONG ulCountPages = 0x1000;
ULONG ulFlagsPage = 0;
APIRET arc = DosQueryMem((void *)pulStackWord,
&ulCountPages,
&ulFlagsPage);
if ( (arc != NO_ERROR)
|| ( (arc == NO_ERROR)
&& ( !( ((ulFlagsPage & (PAG_COMMIT|PAG_READ))
== (PAG_COMMIT|PAG_READ)
)
)
)
)
)
{
fprintf(file, "\n %08lX: ", (ULONG)pulStackWord);
fprintf(file, "Page inaccessible");
pulStackWord += 0x1000;
continue; // for
}
}
sprintf(szAddress, "%08lX",
(ULONG)pulStackWord);
excPrintStackFrame(file,
szAddress,
*(pulStackWord+1));
pulStackWord = (PULONG)*(pulStackWord);
if (pulStackWord == 0)
fprintf(file, "\n pulStackWord == 0");
else if (pulStackWord >= (PULONG)ptib->tib_pstacklimit)
fprintf(file, "\n pulStackWord >= (PULONG)ptib->tib_pstacklimit");
} // end while
}
/*
*@@ excExplainException:
* used by the exception handlers below to write
* LOTS of information about the exception into a logfile.
*
* This calls excPrintStackFrame for each stack frame.
*
*@@changed V0.9.0 [umoeller]: added support for application hook
*@@changed V0.9.0 (99-11-02) [umoeller]: added TID to dump
*@@changed V0.9.2 (2000-03-10) [umoeller]: now using excPrintStackFrame
*@@changed V0.9.3 (2000-05-03) [umoeller]: fixed crashes
*@@changed V0.9.6 (2000-11-06) [umoeller]: added more register dumps
*@@changed V0.9.13 (2001-06-19) [umoeller]: added global flag for whether this is running
*@@changed V0.9.16 (2001-11-02) [pr]: make object display signed
*@@changed V0.9.19 (2002-03-28) [umoeller]: added thread ordinal
*/
VOID excExplainException(FILE *file, // in: logfile from fopen()
PSZ pszHandlerName, // in: descriptive string
PEXCEPTIONREPORTRECORD pReportRec, // in: excpt info
PCONTEXTRECORD pContextRec) // in: excpt info
{
ULONG aulBuf[3];
const char *pcszVersion = "unknown";
PTIB ptib = NULL;
PPIB ppib = NULL;
HMODULE hMod1, hMod2;
CHAR szMod1[CCHMAXPATH] = "unknown",
szMod2[CCHMAXPATH] = "unknown";
ULONG ulObjNum,
ulOffset;
ULONG ul;
ULONG ulOldPriority = 0x0100; // regular, delta 0
// raise global flag for whether this func is running
// V0.9.13 (2001-06-19) [umoeller]
G_ulExplainExceptionRunning++;
// raise this thread's priority, because this
// might take some time
if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
if (ptib)
if (ptib->tib_ptib2)
{
ulOldPriority = ptib->tib_ptib2->tib2_ulpri;
DosSetPriority(PRTYS_THREAD,
PRTYC_REGULAR,
PRTYD_MAXIMUM,
0); // current thread
}
// make some noise
#ifndef __NOEXCEPTIONBEEPS__ // V0.9.19 (2002-04-17) [umoeller]
if (G_fBeepOnException)
{
DosBeep( 250, 30);
DosBeep( 500, 30);
DosBeep(1000, 30);
DosBeep(2000, 30);
DosBeep(4000, 30);
DosBeep(2000, 30);
DosBeep(1000, 30);
DosBeep( 500, 30);
DosBeep( 250, 30);
}
#endif
// generic exception info
DosQuerySysInfo(QSV_VERSION_MAJOR, // 11
QSV_VERSION_MINOR, // 12
&aulBuf, sizeof(aulBuf));
// Warp 3 is reported as 20.30
// Warp 4 is reported as 20.40
// Aurora is reported as 20.45
if (aulBuf[0] == 20)
{
switch (aulBuf[1])
{
case 30: pcszVersion = "Warp 3"; break;
case 40: pcszVersion = "Warp 4"; break;
case 45: pcszVersion = "WSeB kernel"; break;
}
}
fprintf(file,
"Running OS/2 version: %u.%u (%s)\n",
aulBuf[0], // major
aulBuf[1],
pcszVersion);
// generic exception info
fprintf(file,
"\n%s:\n Exception type: %08lX\n Address: %08lX\n Params: ",
pszHandlerName,
pReportRec->ExceptionNum,
(ULONG)pReportRec->ExceptionAddress);
for (ul = 0; ul < pReportRec->cParameters; ul++)
{
fprintf(file, "%08lX ",
pReportRec->ExceptionInfo[ul]);
}
// now explain the exception in a bit more detail;
// depending on the exception, pReportRec->ExceptionInfo
// contains some useful data
switch (pReportRec->ExceptionNum)
{
case XCPT_ACCESS_VIOLATION:
fprintf(file, "\nXCPT_ACCESS_VIOLATION: ");
if (pReportRec->ExceptionInfo[0] & XCPT_READ_ACCESS)
fprintf(file, "Invalid read access from 0x%04lX:%08lX.\n",
pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]);
else if (pReportRec->ExceptionInfo[0] & XCPT_WRITE_ACCESS)
fprintf(file, "Invalid write access to 0x%04lX:%08lX.\n",
pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]);
else if (pReportRec->ExceptionInfo[0] & XCPT_SPACE_ACCESS)
fprintf(file, "Invalid space access at 0x%04lX.\n",
pReportRec->ExceptionInfo[1]);
else if (pReportRec->ExceptionInfo[0] & XCPT_LIMIT_ACCESS)
fprintf(file, "Invalid limit access occurred.\n");
else if (pReportRec->ExceptionInfo[0] == XCPT_UNKNOWN_ACCESS)
fprintf(file, "unknown at 0x%04lX:%08lX\n",
pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]);
fprintf(file,
"Explanation: An attempt was made to access a memory object which does\n"
" not belong to the current process. Most probable causes\n"
" for this are that an invalid pointer was used, there was\n"
" confusion with administering memory or error conditions \n"
" were not properly checked for.\n");
break;
case XCPT_INTEGER_DIVIDE_BY_ZERO:
fprintf(file, "\nXCPT_INTEGER_DIVIDE_BY_ZERO.\n");
fprintf(file,
"Explanation: An attempt was made to divide an integer value by zero,\n"
" which is not defined.\n");
break;
case XCPT_ILLEGAL_INSTRUCTION:
fprintf(file, "\nXCPT_ILLEGAL_INSTRUCTION.\n");
fprintf(file,
"Explanation: An attempt was made to execute an instruction that\n"
" is not defined on this machine's architecture.\n");
break;
case XCPT_PRIVILEGED_INSTRUCTION:
fprintf(file, "\nXCPT_PRIVILEGED_INSTRUCTION.\n");
fprintf(file,
"Explanation: An attempt was made to execute an instruction that\n"
" is not permitted in the current machine mode or that\n"
" the program had no permission to execute.\n");
break;
case XCPT_INTEGER_OVERFLOW:
fprintf(file, "\nXCPT_INTEGER_OVERFLOW.\n");
fprintf(file,
"Explanation: An integer operation generated a carry-out of the most\n"
" significant bit. This is a sign of an attempt to store\n"
" a value which does not fit into an integer variable.\n");
break;
default:
fprintf(file, "\nUnknown OS/2 exception number %d.\n", pReportRec->ExceptionNum);
fprintf(file, "Look this up in the OS/2 header files.\n");
break;
}
// V0.9.16 (2001-11-02) [pr]: We already got this info. above - this overwrites the
// original values before the priority change, which is rather confusing.
// if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
{
/*
* process info:
*
*/
if ((ptib) && (ppib)) // (99-11-01) [umoeller]
{
if (pContextRec->ContextFlags & CONTEXT_CONTROL)
{
// get the main module
hMod1 = ppib->pib_hmte;
DosQueryModuleName(hMod1,
sizeof(szMod1),
szMod1);
// get the trapping module
DosQueryModFromEIP(&hMod2,
&ulObjNum,
sizeof(szMod2),
szMod2,
&ulOffset,
pContextRec->ctx_RegEip);
DosQueryModuleName(hMod2,
sizeof(szMod2),
szMod2);
}
fprintf(file,
"\nProcess information:"
"\n Process ID: 0x%lX"
"\n Process module: 0x%lX (%s)"
"\n Trapping module: 0x%lX (%s)"
"\n Object: %ld\n", // V0.9.16 (2001-11-02) [pr]: make this display signed
ppib->pib_ulpid,
hMod1, szMod1,
hMod2, szMod2,
ulObjNum);
fprintf(file,
"\nTrapping thread information:"
"\n Thread ID: 0x%lX (%lu)"
"\n Thread slot ID: 0x%lX (%lu)" // added V0.9.19 (2002-03-28) [umoeller]
"\n Priority: 0x%lX\n",
ptib->tib_ptib2->tib2_ultid, ptib->tib_ptib2->tib2_ultid,
ptib->tib_ordinal, ptib->tib_ordinal,
ulOldPriority);
}
else
fprintf(file, "\nProcess information was not available.");
/*
* now call the hook, if one has been defined,
* so that the application can write additional
* information to the traplog (V0.9.0)
*/
if (G_pfnExcHook)
G_pfnExcHook(file, ptib, ulOldPriority); // V0.9.16 (2001-12-02) [pr]
// *** registers
fprintf(file, "\nRegisters:");
if (pContextRec->ContextFlags & CONTEXT_INTEGER)
{
// DS the following 4 added V0.9.6 (2000-11-06) [umoeller]
fprintf(file, "\n DS = %08lX ", pContextRec->ctx_SegDs);
excDescribePage(file, pContextRec->ctx_SegDs);
// ES
fprintf(file, "\n ES = %08lX ", pContextRec->ctx_SegEs);
excDescribePage(file, pContextRec->ctx_SegEs);
// FS
fprintf(file, "\n FS = %08lX ", pContextRec->ctx_SegFs);
excDescribePage(file, pContextRec->ctx_SegFs);
// GS
fprintf(file, "\n GS = %08lX ", pContextRec->ctx_SegGs);
excDescribePage(file, pContextRec->ctx_SegGs);
// EAX
fprintf(file, "\n EAX = %08lX ", pContextRec->ctx_RegEax);
excDescribePage(file, pContextRec->ctx_RegEax);
// EBX
fprintf(file, "\n EBX = %08lX ", pContextRec->ctx_RegEbx);
excDescribePage(file, pContextRec->ctx_RegEbx);
// ECX
fprintf(file, "\n ECX = %08lX ", pContextRec->ctx_RegEcx);
excDescribePage(file, pContextRec->ctx_RegEcx);
// EDX
fprintf(file, "\n EDX = %08lX ", pContextRec->ctx_RegEdx);
excDescribePage(file, pContextRec->ctx_RegEdx);
// ESI
fprintf(file, "\n ESI = %08lX ", pContextRec->ctx_RegEsi);
excDescribePage(file, pContextRec->ctx_RegEsi);
// EDI
fprintf(file, "\n EDI = %08lX ", pContextRec->ctx_RegEdi);
excDescribePage(file, pContextRec->ctx_RegEdi);
fprintf(file, "\n");
}
else
fprintf(file, " not available\n");
if (pContextRec->ContextFlags & CONTEXT_CONTROL)
{
// *** instruction
fprintf(file, "Instruction pointer (where exception occured):\n CS:EIP = %04lX:%08lX ",
pContextRec->ctx_SegCs,
pContextRec->ctx_RegEip);
excDescribePage(file, pContextRec->ctx_RegEip);
// *** CPU flags
fprintf(file, "\n EFLAGS = %08lX", pContextRec->ctx_EFlags);
/*
* stack:
*
*/
fprintf(file, "\nStack:\n Base: %08lX\n Limit: %08lX",
(ULONG)(ptib ? ptib->tib_pstack : 0),
(ULONG)(ptib ? ptib->tib_pstacklimit : 0));
fprintf(file, "\n SS:ESP = %04lX:%08lX ",
pContextRec->ctx_SegSs,
pContextRec->ctx_RegEsp);
excDescribePage(file, pContextRec->ctx_RegEsp);
fprintf(file, "\n EBP = %08lX ", pContextRec->ctx_RegEbp);
excDescribePage(file, pContextRec->ctx_RegEbp);
/*
* stack dump:
*/
if (ptib != 0)
{
excDumpStackFrames(file, ptib, pContextRec);
}
}
}
fprintf(file, "\n");
// reset old priority
DosSetPriority(PRTYS_THREAD,
(ulOldPriority & 0x0F00) >> 8,
(UCHAR)ulOldPriority,
0); // current thread
// lower global flag again V0.9.13 (2001-06-19) [umoeller]
G_ulExplainExceptionRunning--;
}
/* ******************************************************************
*
* Exported routines
*
********************************************************************/
/*
*@@ excRegisterHooks:
* this registers hooks which get called for
* exception handlers. You can set any of the
* hooks to NULL for safe defaults (see top of
* except.c for details). You can set none,
* one, or both of the hooks, and you can call
* this function several times.
*
* Both hooks get called whenever an exception
* occurs, so there better be no bugs in these
* routines. ;-) They only get called from
* within excHandlerLoud (because excHandlerQuiet
* writes no trap logs).
*
* The hooks are as follows:
*
* -- pfnExcOpenFileNew gets called to open
* the trap log file. This must return a FILE*
* pointer from fopen(). If this is not defined,
* ?:\TRAP.LOG is used. Use this to specify a
* different file and have some notes written
* into it before the actual exception info.
*
* -- pfnExcHookNew gets called while the trap log
* is being written. At this point,
* the following info has been written into
* the trap log already:
* -- exception type/address block
* -- exception explanation
* -- process information
*
* _After_ the hook, the exception handler
* continues with the "Registers" information
* and stack dump/analysis.
*
* Use this hook to write additional application
* info into the trap log, such as the state
* of your own threads and mutexes.
*
* -- pfnExcHookError gets called when the TRY_* macros
* fail to install an exception handler (when
* DosSetExceptionHandler fails). I've never seen
* this happen.
*
*@@added V0.9.0 [umoeller]
*@@changed V0.9.2 (2000-03-10) [umoeller]: pfnExcHookError added
*/
VOID excRegisterHooks(PFNEXCOPENFILE pfnExcOpenFileNew,
PFNEXCHOOK pfnExcHookNew,
PFNEXCHOOKERROR pfnExcHookError,
BOOL fBeepOnExceptionNew)
{
// adjust the global variables
G_pfnExcOpenFile = pfnExcOpenFileNew;
G_pfnExcHook = pfnExcHookNew;
G_pfnExcHookError = pfnExcHookError;
G_fBeepOnException = fBeepOnExceptionNew;
}
/*
*@@ excHandlerLoud:
* this is the "sophisticated" exception handler;
* which gives forth a loud sequence of beeps thru the
* speaker, writes a trap log and then returns back
* to the thread to continue execution, i.e. the
* default OS/2 exception handler will never get
* called.
*
* This requires a setjmp() call on
* EXCEPTIONREGISTRATIONRECORD2.jmpThread before
* being installed. The TRY_LOUD macro will take
* care of this for you (see except.c).
*
* This intercepts the following exceptions (see
* the OS/2 Control Program Reference for details):
*
* -- XCPT_ACCESS_VIOLATION (traps 0x0d, 0x0e)
* -- XCPT_INTEGER_DIVIDE_BY_ZERO (trap 0)
* -- XCPT_ILLEGAL_INSTRUCTION (trap 6)
* -- XCPT_PRIVILEGED_INSTRUCTION
* -- XCPT_INTEGER_OVERFLOW (trap 4)
*
* For these exceptions, we call the functions in debug.c
* to try to find debug code or SYM file information about
* what source code corresponds to the error.
*
* See excRegisterHooks for the default setup of this.
*
* Note that to get meaningful debugging information
* in this handler's traplog, you need the following:
*
* a) have a MAP file created at link time (/MAP)
*
* b) convert the MAP to a SYM file using MAPSYM
*
* c) put the SYM file in the same directory of
* the module (EXE or DLL). This must have the
* same filestem as the module.
*
* All other exceptions are passed to the next handler
* in the exception handler chain. This might be the
* C/C++ compiler handler or the default OS/2 handler,
* which will probably terminate the process.
*
*@@changed V0.9.0 [umoeller]: added support for thread termination
*@@changed V0.9.2 (2000-03-10) [umoeller]: switched date format to ISO
*@@changed V0.9.19 (2002-05-07) [umoeller]: added EXCEPTIONREPORTRECORD info so that catch block can check that
*/
ULONG _System excHandlerLoud(PEXCEPTIONREPORTRECORD pReportRec,
PEXCEPTIONREGISTRATIONRECORD2 pRegRec2,
PCONTEXTRECORD pContextRec,
PVOID pv)
{
/* From the VAC++3 docs:
* "The first thing an exception handler should do is check the
* exception flags. If EH_EXIT_UNWIND is set, meaning
* the thread is ending, the handler tells the operating system
* to pass the exception to the next exception handler. It does the
* same if the EH_UNWINDING flag is set, the flag that indicates
* this exception handler is being removed.
* The EH_NESTED_CALL flag indicates whether the exception
* occurred within an exception handler. If the handler does
* not check this flag, recursive exceptions could occur until
* there is no stack remaining."
* So for all these conditions, we exit immediately.
*/
if (pReportRec->fHandlerFlags & EH_EXIT_UNWIND)
return (XCPT_CONTINUE_SEARCH);
if (pReportRec->fHandlerFlags & EH_UNWINDING)
return (XCPT_CONTINUE_SEARCH);
if (pReportRec->fHandlerFlags & EH_NESTED_CALL)
return (XCPT_CONTINUE_SEARCH);
switch (pReportRec->ExceptionNum)
{
case XCPT_ACCESS_VIOLATION:
case XCPT_INTEGER_DIVIDE_BY_ZERO:
case XCPT_ILLEGAL_INSTRUCTION:
case XCPT_PRIVILEGED_INSTRUCTION:
case XCPT_INVALID_LOCK_SEQUENCE:
case XCPT_INTEGER_OVERFLOW:
{
// "real" exceptions:
FILE *file;
// open traplog file;
if (G_pfnExcOpenFile)
// hook defined for this: call it
file = (*G_pfnExcOpenFile)();
else
{
CHAR szFileName[100];
// no hook defined: open some
// default traplog file in root directory of
// boot drive
sprintf(szFileName, "%c:\\trap.log", doshQueryBootDrive());
file = fopen(szFileName, "a");
if (file)
{
DATETIME DT;
DosGetDateTime(&DT);
fprintf(file,
"\nTrap message -- Date: %04d-%02d-%02d, Time: %02d:%02d:%02d\n",
DT.year, DT.month, DT.day,
DT.hours, DT.minutes, DT.seconds);
fprintf(file, "------------------------------------------------\n");
}
}
// write error log
excExplainException(file,
"excHandlerLoud",
pReportRec,
pContextRec);
fclose(file);
// copy report rec to user buffer
// V0.9.19 (2002-05-07) [umoeller]
memcpy(&pRegRec2->err,
pReportRec,
sizeof(EXCEPTIONREPORTRECORD));
// jump back to failing routine
longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum);
break; }
}
// not handled
return (XCPT_CONTINUE_SEARCH);
}
/*
*@@ excHandlerQuiet:
* "quiet" xcpt handler, which simply suppresses exceptions;
* this is useful for certain error-prone functions, where
* exceptions are likely to appear, for example used by
* wpshCheckObject to implement a fail-safe SOM object check.
*
* This does _not_ write an error log and makes _no_ sound.
* This simply jumps back to the trapping thread or
* calls EXCEPTIONREGISTRATIONRECORD2.pfnOnKill.
*
* Other than that, this behaves like excHandlerLoud.
*
* This is best registered thru the TRY_QUIET macro
* (new with V0.84, described in except.c), which
* does the necessary setup.
*
*@@changed V0.9.0 [umoeller]: added support for thread termination
*@@changed V0.9.19 (2002-05-07) [umoeller]: added EXCEPTIONREPORTRECORD info so that catch block can check that
*/
ULONG _System excHandlerQuiet(PEXCEPTIONREPORTRECORD pReportRec,
PEXCEPTIONREGISTRATIONRECORD2 pRegRec2,
PCONTEXTRECORD pContextRec,
PVOID pv)
{
if (pReportRec->fHandlerFlags & EH_EXIT_UNWIND)
return (XCPT_CONTINUE_SEARCH);
if (pReportRec->fHandlerFlags & EH_UNWINDING)
return (XCPT_CONTINUE_SEARCH);
if (pReportRec->fHandlerFlags & EH_NESTED_CALL)
return (XCPT_CONTINUE_SEARCH);
switch (pReportRec->ExceptionNum)
{
case XCPT_ACCESS_VIOLATION:
case XCPT_INTEGER_DIVIDE_BY_ZERO:
case XCPT_ILLEGAL_INSTRUCTION:
case XCPT_PRIVILEGED_INSTRUCTION:
case XCPT_INVALID_LOCK_SEQUENCE:
case XCPT_INTEGER_OVERFLOW:
// write excpt explanation only if the
// resp. debugging #define is set (setup.h)
#ifdef DEBUG_WRITEQUIETEXCPT
{
FILE *file = excOpenTraplogFile();
excExplainException(file,
"excHandlerQuiet",
pReportRec,
pContextRec);
fclose(file);
}
#endif
// copy report rec to user buffer
// V0.9.19 (2002-05-07) [umoeller]
memcpy(&pRegRec2->err,
pReportRec,
sizeof(EXCEPTIONREPORTRECORD));
// jump back to failing routine
longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum);
break;
default:
break;
}
return (XCPT_CONTINUE_SEARCH);
}