3 *@@sourcefile except.c:
4 * this file contains powerful exception handlers.
5 * except.h also defines easy-to-use macros for them.
7 * Usage: All OS/2 programs, PM or text mode.
11 * OS/2 exception handlers are a mess to program and,
12 * if installed wrongly, almost impossible to debug.
13 * The problem is that for any program that does a bit
14 * more than showing a message box, using exception
15 * handlers is a must to avoid system hangs. This
16 * especially applies to multi-thread programs using
17 * mutex semaphores (more on that below). The functions
18 * and macros in here are designed to make that more
21 * The macros in except.h automatically insert code for
22 * properly registering and deregistering the handlers
23 * in except.c. You should ALWAYS use these macros
24 * instead of directly registering the handlers to avoid
25 * accidentally forgetting to deregister them. If you
26 * forget to deregister an exception handler, this can
27 * lead to really strange errors (crashes, hangs) which
28 * are nearly impossible to debug because the thread's
29 * stack probably got completely messed up.
31 * The general idea of these macros is to define
32 * TRY / CATCH blocks similar to C++. If an exception
33 * occurs in the TRY block, execution is transferred to
34 * the CATCH block. (This works in both C and C++, by the
37 * The "OnKill" function that was added with V0.9.0 has
38 * been removed again with V0.9.7.
40 * The general usage is like this:
42 + int your_protected_func(int ...)
44 + TRY_LOUD(excptid) // or: TRY_QUIET(excptid)
48 + .... // the stuff in here is protected by
49 + // the excHandlerLoud or excHandlerQuiet
50 + // exception handler
55 + .... // exception occured: react here
56 + } END_CATCH(); // always needed!
57 + } // end of your_func
59 * TRY_LOUD is for installing excHandlerLoud.
60 * TRY_QUIET is for installing excHandlerQuiet.
61 * CATCH / END_CATCH are the same for the two. This
62 * is where the exception handler jumps to if an
64 * The CATCH block is _required_ even if you do nothing
65 * in there, because the CATCH() macro will deregister
68 * "excptid" can be any C identifier which is not used in
69 * your current variable scope, e.g. "excpt1". This
70 * is used for creating an EXCEPTSTRUCT variable of
71 * that name on the stack. The "excptid"'s in TRY_* and
72 * CATCH must match, since this is where the macros
73 * store the exception handler data.
75 * These macros may be nested if you use different
76 * "excptid"'s for sub-macros.
78 * Inside the TRY and CATCH blocks, you must not use
79 * "goto" (to a location outside the block) or "return",
80 * because this will not deregister the handler.
82 * Keep in mind that all the code in the TRY_* block is
83 * protected by the handler, including all functions that
84 * get called. So if you enclose your main() code in a
85 * TRY_* block, your entire application is protected.
86 * If any subfunction fails, execution is transferred to
87 * the closest CATCH() that was installed (as with C++
90 * <B>Asynchronous exceptions</B>
92 * The exception handlers in this file (which are installed
93 * with the TRY/CATCH mechanism) only intercept synchronous
94 * exceptions, most importantly, XCPT_ACCESS_VIOLATION (see
95 * excHandlerLoud for a list). They do not protect your code
96 * against asynchronous exceptions.
98 * OS/2 defines asynchronous exceptions to be those that
99 * can be delayed. With OS/2, there are only three of these:
101 * -- XCPT_PROCESS_TERMINATE
102 * -- XCPT_ASYNC_PROCESS_TERMINATE
103 * -- XCPT_SIGNAL (thread 1 only)
105 * To protect yourself against these also, put the section
106 * in question in a DosEnterMustComplete/DosExitMustComplete
109 * <B>Mutex semaphores</B>
111 * The problem with OS/2 mutex semaphores is that they are
112 * sometimes not automatically released when a thread terminates.
113 * If there are several mutexes involved and they are released
114 * in improper order, you can get zombie threads on exit.
115 * Even worse, if this happens to a PM thread, this will hang
118 * As a result, you should protect any section of code which
119 * requests a semaphore with the exception handlers.
121 * So _whenever_ you request a mutex semaphore, enclose
122 * the block with TRY/CATCH in case the code crashes.
123 * Besides, enclose the TRY/CATCH block in a must-complete
124 * section, like this:
130 + BOOL fSemOwned = FALSE;
132 + TRY_QUIET(excpt1) // or TRY_LOUD
134 + if (fSemOwned = !DosRequestMutexSem(hmtx, ...))
135 + { ... // work on your protected data
137 + // mutex gets released below
139 + CATCH(excpt1) { } END_CATCH(); // always needed!
142 + // this gets executed always, even if an exception occured
143 + DosReleaseMutexSem(hmtx);
144 + } // end of your_func
146 * This way your mutex semaphore gets released in every
147 * possible condition.
151 * As opposed to versions before 0.9.0, this code is now
152 * completely independent of XWorkplace. This file now
153 * contains "pure" exception handlers only.
155 * However, you can customize these exception handlers by
156 * calling excRegisterHooks. This is what XWorkplace does now.
157 * This should be done upon initialization of your application.
158 * If excRegisterHooks is not called, the following safe
161 * -- the trap log file is TRAP.LOG in the root
162 * directory of your boot drive.
164 * For details on the provided exception handlers, refer
165 * to excHandlerLoud and excHandlerQuiet.
167 * More useful debug information can be found in the "OS/2 Debugging
168 * Handbook", which is now available in INF format on the IBM
169 * DevCon site ("http://service2.boulder.ibm.com/devcon/").
170 * This book shows worked examples of how to unwind a stack dump.
172 * This file incorporates code from the following:
173 * -- Monte Copeland, IBM Boca Ration, Florida, USA (1993)
174 * -- Roman Stangl, from the Program Commander/2 sources
176 * -- Marc Fiammante, John Currier, Kim Rasmussen,
177 * Anthony Cruise (EXCEPT3.ZIP package for a generic
178 * exception handling DLL, available at Hobbes).
180 * If not explicitly stated otherwise, the code has been written
181 * by me, Ulrich M”ller.
183 * Note: Version numbering in this file relates to XWorkplace version
186 *@@header "helpers\except.h"
190 * This file Copyright (C) 1992-99 Ulrich M”ller,
197 * This file is part of the "XWorkplace helpers" source package.
198 * This is free software; you can redistribute it and/or modify
199 * it under the terms of the GNU General Public License as published
200 * by the Free Software Foundation, in version 2 as it comes in the
201 * "COPYING" file of the XWorkplace main distribution.
202 * This program is distributed in the hope that it will be useful,
203 * but WITHOUT ANY WARRANTY; without even the implied warranty of
204 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
205 * GNU General Public License for more details.
208 #define OS2EMX_PLAIN_CHAR
209 // this is needed for "os2emx.h"; if this is defined,
210 // emx will define PSZ as _signed_ char, otherwise
213 #define INCL_DOSMODULEMGR
214 #define INCL_DOSEXCEPTIONS
215 #define INCL_DOSPROCESS
217 #define INCL_DOSERRORS
221 #include <stdio.h> // needed for except.h
225 #include <setjmp.h> // needed for except.h
226 #include <assert.h> // needed for except.h
228 #define DONT_REPLACE_MALLOC
229 #include "helpers\setup.h" // code generation and debugging options
231 // headers in /helpers
232 #include "helpers\dosh.h" // Control Program helper routines
233 #include "helpers\except.h" // exception handling
234 #include "helpers\debug.h" // symbol/debug code analysis
238 /* ******************************************************************
242 ********************************************************************/
244 // hooks to be registered using excRegisterHooks
245 PFNEXCOPENFILE G_pfnExcOpenFile
= 0;
246 PFNEXCHOOK G_pfnExcHook
= 0;
247 PFNEXCHOOKERROR G_pfnExcHookError
= 0;
248 // beep flag for excHandlerLoud
249 BOOL G_fBeepOnException
= TRUE
;
251 ULONG G_ulExplainExceptionRunning
= 0;
252 // global flag which is != 0 if some exception handler
253 // is inside excExplainException, so that XShutdown can
254 // wait until the trap log is done;
255 // this is exported thru except.h
256 // V0.9.13 (2001-06-19) [umoeller]
259 *@@category: Helpers\Control program helpers\Exceptions/debugging
263 /* ******************************************************************
265 * Exception helper routines
267 ********************************************************************/
274 VOID
excDescribePage(FILE *file
, ULONG ulCheck
)
277 ULONG ulCountPages
= 1;
278 ULONG ulFlagsPage
= 0;
279 arc
= DosQueryMem((PVOID
)ulCheck
, &ulCountPages
, &ulFlagsPage
);
283 fprintf(file
, "valid, flags: ");
284 if (ulFlagsPage
& PAG_READ
)
285 fprintf(file
, "read ");
286 if (ulFlagsPage
& PAG_WRITE
)
287 fprintf(file
, "write ");
288 if (ulFlagsPage
& PAG_EXECUTE
)
289 fprintf(file
, "execute ");
290 if (ulFlagsPage
& PAG_GUARD
)
291 fprintf(file
, "guard ");
292 if (ulFlagsPage
& PAG_COMMIT
)
293 fprintf(file
, "committed ");
294 if (ulFlagsPage
& PAG_SHARED
)
295 fprintf(file
, "shared ");
296 if (ulFlagsPage
& PAG_FREE
)
297 fprintf(file
, "free ");
298 if (ulFlagsPage
& PAG_BASE
)
299 fprintf(file
, "base ");
301 else if (arc
== ERROR_INVALID_ADDRESS
)
302 fprintf(file
, "invalid");
306 *@@ excPrintStackFrame:
307 * wrapper for dbgPrintStackFrame to format
308 * output stuff right.
310 *@@added V0.9.2 (2000-03-10) [umoeller]
311 *@@changed V0.9.12 (2001-05-12) [umoeller]: added seg:ofs to output always
314 VOID
excPrintStackFrame(FILE *file
, // in: output log file
315 PSZ pszDescription
, // in: description for stack frame (should be eight chars)
316 ULONG ulAddress
) // in: address to debug
318 APIRET arc
= NO_ERROR
;
319 HMODULE hmod1
= NULLHANDLE
;
320 CHAR szMod1
[2*CCHMAXPATH
] = "unknown";
327 arc
= DosQueryModFromEIP(&hmod1
,
329 sizeof(szMod1
), szMod1
,
337 " %-8s Error: DosQueryModFromEIP returned %lu\n",
343 CHAR szFullName
[2*CCHMAXPATH
];
346 " %-8s %02lX:%08lX\n ",
348 ulObject
+ 1, // V0.9.12 (2001-05-12) [umoeller]
349 ulOffset
); // V0.9.12 (2001-05-12) [umoeller]
351 DosQueryModuleName(hmod1
, sizeof(szFullName
), szFullName
);
352 dbgPrintStackFrame(file
,
359 // make a 'tick' sound to let the user know we're still alive
365 *@@ excDumpStackFrames:
366 * called from excExplainException to dump the
367 * thread's stack frames. This calls excPrintStackFrame
368 * for each stack frame found.
370 *@@added V0.9.4 (2000-06-15) [umoeller]
373 VOID
excDumpStackFrames(FILE *file
, // in: logfile from fopen()
375 PCONTEXTRECORD pContextRec
) // in: excpt info
377 PULONG pulStackWord
= 0;
379 fprintf(file
, "\n\nStack frames:\n Address Module seg:ofs\n");
381 // first the trapping address itself
382 excPrintStackFrame(file
,
384 pContextRec
->ctx_RegEip
);
387 pulStackWord
= (PULONG
)pContextRec
->ctx_RegEbp
;
388 /* if (pContextRec->ctx_RegEbp < pContextRec->ctx_RegEsp)
389 pulStackWord = (PULONG)(pContextRec->ctx_RegEbp & 0xFFFFFFF0);
391 pulStackWord = (PULONG)(pContextRec->ctx_RegEsp & 0xFFFFFFF0); */
393 while ( (pulStackWord
!= 0)
394 && (pulStackWord
< (PULONG
)ptib
->tib_pstacklimit
)
399 if (((ULONG
)pulStackWord
& 0x00000FFF) == 0x00000000)
401 // we're on a page boundary: check access
402 ULONG ulCountPages
= 0x1000;
403 ULONG ulFlagsPage
= 0;
404 APIRET arc
= DosQueryMem((void *)pulStackWord
,
407 if ( (arc
!= NO_ERROR
)
408 || ( (arc
== NO_ERROR
)
409 && ( !( ((ulFlagsPage
& (PAG_COMMIT
|PAG_READ
))
410 == (PAG_COMMIT
|PAG_READ
)
417 fprintf(file
, "\n %08lX: ", (ULONG
)pulStackWord
);
418 fprintf(file
, "Page inaccessible");
419 pulStackWord
+= 0x1000;
424 sprintf(szAddress
, "%08lX",
425 (ULONG
)pulStackWord
);
426 excPrintStackFrame(file
,
429 pulStackWord
= (PULONG
)*(pulStackWord
);
431 if (pulStackWord
== 0)
432 fprintf(file
, "\n pulStackWord == 0");
433 else if (pulStackWord
>= (PULONG
)ptib
->tib_pstacklimit
)
434 fprintf(file
, "\n pulStackWord >= (PULONG)ptib->tib_pstacklimit");
439 *@@ excExplainException:
440 * used by the exception handlers below to write
441 * LOTS of information about the exception into a logfile.
443 * This calls excPrintStackFrame for each stack frame.
445 *@@changed V0.9.0 [umoeller]: added support for application hook
446 *@@changed V0.9.0 (99-11-02) [umoeller]: added TID to dump
447 *@@changed V0.9.2 (2000-03-10) [umoeller]: now using excPrintStackFrame
448 *@@changed V0.9.3 (2000-05-03) [umoeller]: fixed crashes
449 *@@changed V0.9.6 (2000-11-06) [umoeller]: added more register dumps
450 *@@changed V0.9.13 (2001-06-19) [umoeller]: added global flag for whether this is running
451 *@@changed V0.9.16 (2001-11-02) [pr]: make object display signed
452 *@@changed V0.9.19 (2002-03-28) [umoeller]: added thread ordinal
455 VOID
excExplainException(FILE *file
, // in: logfile from fopen()
456 PSZ pszHandlerName
, // in: descriptive string
457 PEXCEPTIONREPORTRECORD pReportRec
, // in: excpt info
458 PCONTEXTRECORD pContextRec
) // in: excpt info
461 const char *pcszVersion
= "unknown";
465 HMODULE hMod1
, hMod2
;
466 CHAR szMod1
[CCHMAXPATH
] = "unknown",
467 szMod2
[CCHMAXPATH
] = "unknown";
472 ULONG ulOldPriority
= 0x0100; // regular, delta 0
474 // raise global flag for whether this func is running
475 // V0.9.13 (2001-06-19) [umoeller]
476 G_ulExplainExceptionRunning
++;
478 // raise this thread's priority, because this
479 // might take some time
480 if (DosGetInfoBlocks(&ptib
, &ppib
) == NO_ERROR
)
484 ulOldPriority
= ptib
->tib_ptib2
->tib2_ulpri
;
485 DosSetPriority(PRTYS_THREAD
,
488 0); // current thread
492 #ifndef __NOEXCEPTIONBEEPS__ // V0.9.19 (2002-04-17) [umoeller]
493 if (G_fBeepOnException
)
507 // generic exception info
508 DosQuerySysInfo(QSV_VERSION_MAJOR
, // 11
509 QSV_VERSION_MINOR
, // 12
510 &aulBuf
, sizeof(aulBuf
));
511 // Warp 3 is reported as 20.30
512 // Warp 4 is reported as 20.40
513 // Aurora is reported as 20.45
519 case 30: pcszVersion
= "Warp 3"; break;
520 case 40: pcszVersion
= "Warp 4"; break;
521 case 45: pcszVersion
= "WSeB kernel"; break;
525 "Running OS/2 version: %u.%u (%s)\n",
531 // generic exception info
533 "\n%s:\n Exception type: %08lX\n Address: %08lX\n Params: ",
535 pReportRec
->ExceptionNum
,
536 (ULONG
)pReportRec
->ExceptionAddress
);
537 for (ul
= 0; ul
< pReportRec
->cParameters
; ul
++)
539 fprintf(file
, "%08lX ",
540 pReportRec
->ExceptionInfo
[ul
]);
543 // now explain the exception in a bit more detail;
544 // depending on the exception, pReportRec->ExceptionInfo
545 // contains some useful data
546 switch (pReportRec
->ExceptionNum
)
548 case XCPT_ACCESS_VIOLATION
:
549 fprintf(file
, "\nXCPT_ACCESS_VIOLATION: ");
550 if (pReportRec
->ExceptionInfo
[0] & XCPT_READ_ACCESS
)
551 fprintf(file
, "Invalid read access from 0x%04lX:%08lX.\n",
552 pContextRec
->ctx_SegDs
, pReportRec
->ExceptionInfo
[1]);
553 else if (pReportRec
->ExceptionInfo
[0] & XCPT_WRITE_ACCESS
)
554 fprintf(file
, "Invalid write access to 0x%04lX:%08lX.\n",
555 pContextRec
->ctx_SegDs
, pReportRec
->ExceptionInfo
[1]);
556 else if (pReportRec
->ExceptionInfo
[0] & XCPT_SPACE_ACCESS
)
557 fprintf(file
, "Invalid space access at 0x%04lX.\n",
558 pReportRec
->ExceptionInfo
[1]);
559 else if (pReportRec
->ExceptionInfo
[0] & XCPT_LIMIT_ACCESS
)
560 fprintf(file
, "Invalid limit access occurred.\n");
561 else if (pReportRec
->ExceptionInfo
[0] == XCPT_UNKNOWN_ACCESS
)
562 fprintf(file
, "unknown at 0x%04lX:%08lX\n",
563 pContextRec
->ctx_SegDs
, pReportRec
->ExceptionInfo
[1]);
565 "Explanation: An attempt was made to access a memory object which does\n"
566 " not belong to the current process. Most probable causes\n"
567 " for this are that an invalid pointer was used, there was\n"
568 " confusion with administering memory or error conditions \n"
569 " were not properly checked for.\n");
572 case XCPT_INTEGER_DIVIDE_BY_ZERO
:
573 fprintf(file
, "\nXCPT_INTEGER_DIVIDE_BY_ZERO.\n");
575 "Explanation: An attempt was made to divide an integer value by zero,\n"
576 " which is not defined.\n");
579 case XCPT_ILLEGAL_INSTRUCTION
:
580 fprintf(file
, "\nXCPT_ILLEGAL_INSTRUCTION.\n");
582 "Explanation: An attempt was made to execute an instruction that\n"
583 " is not defined on this machine's architecture.\n");
586 case XCPT_PRIVILEGED_INSTRUCTION
:
587 fprintf(file
, "\nXCPT_PRIVILEGED_INSTRUCTION.\n");
589 "Explanation: An attempt was made to execute an instruction that\n"
590 " is not permitted in the current machine mode or that\n"
591 " the program had no permission to execute.\n");
594 case XCPT_INTEGER_OVERFLOW
:
595 fprintf(file
, "\nXCPT_INTEGER_OVERFLOW.\n");
597 "Explanation: An integer operation generated a carry-out of the most\n"
598 " significant bit. This is a sign of an attempt to store\n"
599 " a value which does not fit into an integer variable.\n");
603 fprintf(file
, "\nUnknown OS/2 exception number %d.\n", pReportRec
->ExceptionNum
);
604 fprintf(file
, "Look this up in the OS/2 header files.\n");
608 // V0.9.16 (2001-11-02) [pr]: We already got this info. above - this overwrites the
609 // original values before the priority change, which is rather confusing.
610 // if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
617 if ((ptib
) && (ppib
)) // (99-11-01) [umoeller]
619 if (pContextRec
->ContextFlags
& CONTEXT_CONTROL
)
621 // get the main module
622 hMod1
= ppib
->pib_hmte
;
623 DosQueryModuleName(hMod1
,
627 // get the trapping module
628 DosQueryModFromEIP(&hMod2
,
633 pContextRec
->ctx_RegEip
);
634 DosQueryModuleName(hMod2
,
640 "\nProcess information:"
641 "\n Process ID: 0x%lX"
642 "\n Process module: 0x%lX (%s)"
643 "\n Trapping module: 0x%lX (%s)"
644 "\n Object: %ld\n", // V0.9.16 (2001-11-02) [pr]: make this display signed
651 "\nTrapping thread information:"
652 "\n Thread ID: 0x%lX (%lu)"
653 "\n Thread slot ID: 0x%lX (%lu)" // added V0.9.19 (2002-03-28) [umoeller]
654 "\n Priority: 0x%lX\n",
655 ptib
->tib_ptib2
->tib2_ultid
, ptib
->tib_ptib2
->tib2_ultid
,
656 ptib
->tib_ordinal
, ptib
->tib_ordinal
,
660 fprintf(file
, "\nProcess information was not available.");
663 * now call the hook, if one has been defined,
664 * so that the application can write additional
665 * information to the traplog (V0.9.0)
669 G_pfnExcHook(file
, ptib
, ulOldPriority
); // V0.9.16 (2001-12-02) [pr]
673 fprintf(file
, "\nRegisters:");
674 if (pContextRec
->ContextFlags
& CONTEXT_INTEGER
)
676 // DS the following 4 added V0.9.6 (2000-11-06) [umoeller]
677 fprintf(file
, "\n DS = %08lX ", pContextRec
->ctx_SegDs
);
678 excDescribePage(file
, pContextRec
->ctx_SegDs
);
680 fprintf(file
, "\n ES = %08lX ", pContextRec
->ctx_SegEs
);
681 excDescribePage(file
, pContextRec
->ctx_SegEs
);
683 fprintf(file
, "\n FS = %08lX ", pContextRec
->ctx_SegFs
);
684 excDescribePage(file
, pContextRec
->ctx_SegFs
);
686 fprintf(file
, "\n GS = %08lX ", pContextRec
->ctx_SegGs
);
687 excDescribePage(file
, pContextRec
->ctx_SegGs
);
690 fprintf(file
, "\n EAX = %08lX ", pContextRec
->ctx_RegEax
);
691 excDescribePage(file
, pContextRec
->ctx_RegEax
);
693 fprintf(file
, "\n EBX = %08lX ", pContextRec
->ctx_RegEbx
);
694 excDescribePage(file
, pContextRec
->ctx_RegEbx
);
696 fprintf(file
, "\n ECX = %08lX ", pContextRec
->ctx_RegEcx
);
697 excDescribePage(file
, pContextRec
->ctx_RegEcx
);
699 fprintf(file
, "\n EDX = %08lX ", pContextRec
->ctx_RegEdx
);
700 excDescribePage(file
, pContextRec
->ctx_RegEdx
);
702 fprintf(file
, "\n ESI = %08lX ", pContextRec
->ctx_RegEsi
);
703 excDescribePage(file
, pContextRec
->ctx_RegEsi
);
705 fprintf(file
, "\n EDI = %08lX ", pContextRec
->ctx_RegEdi
);
706 excDescribePage(file
, pContextRec
->ctx_RegEdi
);
710 fprintf(file
, " not available\n");
712 if (pContextRec
->ContextFlags
& CONTEXT_CONTROL
)
717 fprintf(file
, "Instruction pointer (where exception occured):\n CS:EIP = %04lX:%08lX ",
718 pContextRec
->ctx_SegCs
,
719 pContextRec
->ctx_RegEip
);
720 excDescribePage(file
, pContextRec
->ctx_RegEip
);
724 fprintf(file
, "\n EFLAGS = %08lX", pContextRec
->ctx_EFlags
);
731 fprintf(file
, "\nStack:\n Base: %08lX\n Limit: %08lX",
732 (ULONG
)(ptib
? ptib
->tib_pstack
: 0),
733 (ULONG
)(ptib
? ptib
->tib_pstacklimit
: 0));
734 fprintf(file
, "\n SS:ESP = %04lX:%08lX ",
735 pContextRec
->ctx_SegSs
,
736 pContextRec
->ctx_RegEsp
);
737 excDescribePage(file
, pContextRec
->ctx_RegEsp
);
739 fprintf(file
, "\n EBP = %08lX ", pContextRec
->ctx_RegEbp
);
740 excDescribePage(file
, pContextRec
->ctx_RegEbp
);
748 excDumpStackFrames(file
, ptib
, pContextRec
);
754 // reset old priority
755 DosSetPriority(PRTYS_THREAD
,
756 (ulOldPriority
& 0x0F00) >> 8,
757 (UCHAR
)ulOldPriority
,
758 0); // current thread
760 // lower global flag again V0.9.13 (2001-06-19) [umoeller]
761 G_ulExplainExceptionRunning
--;
764 /* ******************************************************************
768 ********************************************************************/
771 *@@ excRegisterHooks:
772 * this registers hooks which get called for
773 * exception handlers. You can set any of the
774 * hooks to NULL for safe defaults (see top of
775 * except.c for details). You can set none,
776 * one, or both of the hooks, and you can call
777 * this function several times.
779 * Both hooks get called whenever an exception
780 * occurs, so there better be no bugs in these
781 * routines. ;-) They only get called from
782 * within excHandlerLoud (because excHandlerQuiet
783 * writes no trap logs).
785 * The hooks are as follows:
787 * -- pfnExcOpenFileNew gets called to open
788 * the trap log file. This must return a FILE*
789 * pointer from fopen(). If this is not defined,
790 * ?:\TRAP.LOG is used. Use this to specify a
791 * different file and have some notes written
792 * into it before the actual exception info.
794 * -- pfnExcHookNew gets called while the trap log
795 * is being written. At this point,
796 * the following info has been written into
797 * the trap log already:
798 * -- exception type/address block
799 * -- exception explanation
800 * -- process information
802 * _After_ the hook, the exception handler
803 * continues with the "Registers" information
804 * and stack dump/analysis.
806 * Use this hook to write additional application
807 * info into the trap log, such as the state
808 * of your own threads and mutexes.
810 * -- pfnExcHookError gets called when the TRY_* macros
811 * fail to install an exception handler (when
812 * DosSetExceptionHandler fails). I've never seen
815 *@@added V0.9.0 [umoeller]
816 *@@changed V0.9.2 (2000-03-10) [umoeller]: pfnExcHookError added
819 VOID
excRegisterHooks(PFNEXCOPENFILE pfnExcOpenFileNew
,
820 PFNEXCHOOK pfnExcHookNew
,
821 PFNEXCHOOKERROR pfnExcHookError
,
822 BOOL fBeepOnExceptionNew
)
824 // adjust the global variables
825 G_pfnExcOpenFile
= pfnExcOpenFileNew
;
826 G_pfnExcHook
= pfnExcHookNew
;
827 G_pfnExcHookError
= pfnExcHookError
;
828 G_fBeepOnException
= fBeepOnExceptionNew
;
833 * this is the "sophisticated" exception handler;
834 * which gives forth a loud sequence of beeps thru the
835 * speaker, writes a trap log and then returns back
836 * to the thread to continue execution, i.e. the
837 * default OS/2 exception handler will never get
840 * This requires a setjmp() call on
841 * EXCEPTIONREGISTRATIONRECORD2.jmpThread before
842 * being installed. The TRY_LOUD macro will take
843 * care of this for you (see except.c).
845 * This intercepts the following exceptions (see
846 * the OS/2 Control Program Reference for details):
848 * -- XCPT_ACCESS_VIOLATION (traps 0x0d, 0x0e)
849 * -- XCPT_INTEGER_DIVIDE_BY_ZERO (trap 0)
850 * -- XCPT_ILLEGAL_INSTRUCTION (trap 6)
851 * -- XCPT_PRIVILEGED_INSTRUCTION
852 * -- XCPT_INTEGER_OVERFLOW (trap 4)
854 * For these exceptions, we call the functions in debug.c
855 * to try to find debug code or SYM file information about
856 * what source code corresponds to the error.
858 * See excRegisterHooks for the default setup of this.
860 * Note that to get meaningful debugging information
861 * in this handler's traplog, you need the following:
863 * a) have a MAP file created at link time (/MAP)
865 * b) convert the MAP to a SYM file using MAPSYM
867 * c) put the SYM file in the same directory of
868 * the module (EXE or DLL). This must have the
869 * same filestem as the module.
871 * All other exceptions are passed to the next handler
872 * in the exception handler chain. This might be the
873 * C/C++ compiler handler or the default OS/2 handler,
874 * which will probably terminate the process.
876 *@@changed V0.9.0 [umoeller]: added support for thread termination
877 *@@changed V0.9.2 (2000-03-10) [umoeller]: switched date format to ISO
878 *@@changed V0.9.19 (2002-05-07) [umoeller]: added EXCEPTIONREPORTRECORD info so that catch block can check that
881 ULONG _System
excHandlerLoud(PEXCEPTIONREPORTRECORD pReportRec
,
882 PEXCEPTIONREGISTRATIONRECORD2 pRegRec2
,
883 PCONTEXTRECORD pContextRec
,
886 /* From the VAC++3 docs:
887 * "The first thing an exception handler should do is check the
888 * exception flags. If EH_EXIT_UNWIND is set, meaning
889 * the thread is ending, the handler tells the operating system
890 * to pass the exception to the next exception handler. It does the
891 * same if the EH_UNWINDING flag is set, the flag that indicates
892 * this exception handler is being removed.
893 * The EH_NESTED_CALL flag indicates whether the exception
894 * occurred within an exception handler. If the handler does
895 * not check this flag, recursive exceptions could occur until
896 * there is no stack remaining."
897 * So for all these conditions, we exit immediately.
900 if (pReportRec
->fHandlerFlags
& EH_EXIT_UNWIND
)
901 return (XCPT_CONTINUE_SEARCH
);
902 if (pReportRec
->fHandlerFlags
& EH_UNWINDING
)
903 return (XCPT_CONTINUE_SEARCH
);
904 if (pReportRec
->fHandlerFlags
& EH_NESTED_CALL
)
905 return (XCPT_CONTINUE_SEARCH
);
907 switch (pReportRec
->ExceptionNum
)
909 case XCPT_ACCESS_VIOLATION
:
910 case XCPT_INTEGER_DIVIDE_BY_ZERO
:
911 case XCPT_ILLEGAL_INSTRUCTION
:
912 case XCPT_PRIVILEGED_INSTRUCTION
:
913 case XCPT_INVALID_LOCK_SEQUENCE
:
914 case XCPT_INTEGER_OVERFLOW
:
916 // "real" exceptions:
919 // open traplog file;
920 if (G_pfnExcOpenFile
)
921 // hook defined for this: call it
922 file
= (*G_pfnExcOpenFile
)();
925 CHAR szFileName
[100];
926 // no hook defined: open some
927 // default traplog file in root directory of
929 sprintf(szFileName
, "%c:\\trap.log", doshQueryBootDrive());
930 file
= fopen(szFileName
, "a");
937 "\nTrap message -- Date: %04d-%02d-%02d, Time: %02d:%02d:%02d\n",
938 DT
.year
, DT
.month
, DT
.day
,
939 DT
.hours
, DT
.minutes
, DT
.seconds
);
940 fprintf(file
, "------------------------------------------------\n");
946 excExplainException(file
,
952 // copy report rec to user buffer
953 // V0.9.19 (2002-05-07) [umoeller]
954 memcpy(&pRegRec2
->err
,
956 sizeof(EXCEPTIONREPORTRECORD
));
958 // jump back to failing routine
959 longjmp(pRegRec2
->jmpThread
, pReportRec
->ExceptionNum
);
964 return (XCPT_CONTINUE_SEARCH
);
969 * "quiet" xcpt handler, which simply suppresses exceptions;
970 * this is useful for certain error-prone functions, where
971 * exceptions are likely to appear, for example used by
972 * wpshCheckObject to implement a fail-safe SOM object check.
974 * This does _not_ write an error log and makes _no_ sound.
975 * This simply jumps back to the trapping thread or
976 * calls EXCEPTIONREGISTRATIONRECORD2.pfnOnKill.
978 * Other than that, this behaves like excHandlerLoud.
980 * This is best registered thru the TRY_QUIET macro
981 * (new with V0.84, described in except.c), which
982 * does the necessary setup.
984 *@@changed V0.9.0 [umoeller]: added support for thread termination
985 *@@changed V0.9.19 (2002-05-07) [umoeller]: added EXCEPTIONREPORTRECORD info so that catch block can check that
988 ULONG _System
excHandlerQuiet(PEXCEPTIONREPORTRECORD pReportRec
,
989 PEXCEPTIONREGISTRATIONRECORD2 pRegRec2
,
990 PCONTEXTRECORD pContextRec
,
993 if (pReportRec
->fHandlerFlags
& EH_EXIT_UNWIND
)
994 return (XCPT_CONTINUE_SEARCH
);
995 if (pReportRec
->fHandlerFlags
& EH_UNWINDING
)
996 return (XCPT_CONTINUE_SEARCH
);
997 if (pReportRec
->fHandlerFlags
& EH_NESTED_CALL
)
998 return (XCPT_CONTINUE_SEARCH
);
1000 switch (pReportRec
->ExceptionNum
)
1002 case XCPT_ACCESS_VIOLATION
:
1003 case XCPT_INTEGER_DIVIDE_BY_ZERO
:
1004 case XCPT_ILLEGAL_INSTRUCTION
:
1005 case XCPT_PRIVILEGED_INSTRUCTION
:
1006 case XCPT_INVALID_LOCK_SEQUENCE
:
1007 case XCPT_INTEGER_OVERFLOW
:
1008 // write excpt explanation only if the
1009 // resp. debugging #define is set (setup.h)
1010 #ifdef DEBUG_WRITEQUIETEXCPT
1012 FILE *file
= excOpenTraplogFile();
1013 excExplainException(file
,
1021 // copy report rec to user buffer
1022 // V0.9.19 (2002-05-07) [umoeller]
1023 memcpy(&pRegRec2
->err
,
1025 sizeof(EXCEPTIONREPORTRECORD
));
1027 // jump back to failing routine
1028 longjmp(pRegRec2
->jmpThread
, pReportRec
->ExceptionNum
);
1035 return (XCPT_CONTINUE_SEARCH
);