source: trunk/src/helpers/except.c@ 8

Last change on this file since 8 was 8, checked in by umoeller, 25 years ago

Initial checkin of helpers code which used to be in WarpIN.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 36.4 KB
Line 
1
2/*
3 *@@sourcefile except.c:
4 * this file contains powerful exception handlers.
5 *
6 * Usage: All OS/2 programs, PM or text mode.
7 *
8 * <B>except.h macros</B>
9 *
10 * except.h declares a few handy macros to easily install
11 * exception handling for a section of code.
12 *
13 * These macros automatically insert code for properly
14 * registering and deregistering the handlers. You should
15 * ALWAYS use these macros instead of directly registering
16 * the handlers to avoid accidentally forgetting to deregister
17 * them. If you forget to deregister an exception handler, this
18 * can lead to really strange errors (crashes, hangs) which are
19 * nearly impossible to debug because the thread's stack space
20 * might get completely messed up.
21 *
22 * The general idea of these macros is to define
23 * TRY / CATCH blocks similar to C++. If an exception
24 * occurs in the TRY block, execution is transferred
25 * to the CATCH block. (This works in both C and C++,
26 * by the way.)
27 *
28 * In addition, with the TRY statement, you may specify
29 * an optional "OnKill" function which gets called if
30 * the thread gets killed while code in the TRY block is
31 * executed (new with V0.9.0). If you need no such
32 * function, specify NULL. Details follow.
33 *
34 * The general usage is like this:
35 *
36 + int your_protected_func(int ...)
37 + {
38 + TRY_LOUD(excptid, OnKillFunc)
39 + // or: TRY_QUIET(excptid, OnKillFunc)
40 + // OnKillFunc can be NULL
41 + {
42 + .... // the stuff in here is protected by
43 + // the excHandlerLoud or excHandlerQuiet
44 + // exception handler
45 + }
46 + CATCH(excptid)
47 + {
48 + .... // exception occured: react here
49 + } END_CATCH(); // always needed!
50 + } // end of your_func
51 *
52 * TRY_LOUD is for excHandlerLoud (see below).
53 * TRY_QUIET is for excHandlerQuiet (see below).
54 * CATCH / END_CATCH are the same for the two. This
55 * is where the exception handler jumps to if an
56 * exception occurs.
57 * The CATCH block is _required_ even if you do nothing
58 * in there.
59 *
60 * "excptid" can be any identifier which is not used in
61 * your current variable scope, e.g. "excpt1". This
62 * is used for creating an EXCEPTSTRUCT variable of
63 * that name on the stack. The "excptid"'s in TRY_* and
64 * CATCH must match, since this is where the macros
65 * store the exception handler data.
66 *
67 * It is possible to nest these handlers though. You
68 * must use different "excptid"'s only if more than one
69 * TRY_* block exists in the same variable scope. Avoid
70 * using gotos to jump between the different CATCH blocks,
71 * because this might not properly deregister the handlers.
72 *
73 * Keep in mind that all the code in the TRY_* block is
74 * protected by the handler, including all functions that
75 * get called. So if you enclose your main() code in a
76 * TRY_* block, your entire application is protected.
77 *
78 * Your "OnKillFunc" must be declared as
79 + VOID APIENTRY YourOnKillFunc(VOID) {...}.
80 * This gets called when the thread gets terminated
81 * while being executed in the TRY block (that is,
82 * if either the thread gets killed explicitly or
83 * the entire process ends for some reason). After
84 * that function got executed, the thread will _always_
85 * be terminated; the CATCH block will not be executed
86 * then. You can specify NULL for the "OnKillFunc", if
87 * you don't need one.
88 *
89 * To summarize: If an exception occurs in the TRY
90 * block,
91 *
92 * a) "OnKillFunc" gets called if the thread got
93 * killed, and the thread then terminates;
94 *
95 * b) the CATCH block is executed for other exceptions
96 * and execution continues.
97 *
98 * <B>Mutex semaphores</B>
99 *
100 * Here's how to deal with mutex semaphores: WHENEVER
101 * you request a mutex semaphore, enclose the block
102 * with TRY/CATCH in case an error occurs, like this:
103 *
104 + HMTX hmtx = ...
105 +
106 + VOID APIENTRY OnKillYourFunc(VOID)
107 + {
108 + DosReleaseMutexSem(hmtx);
109 + }
110 +
111 + int your_func(int)
112 + {
113 + BOOL fSemOwned = FALSE;
114 + TRY_QUIET(excpt1, OnKillYourFunc) // or TRY_LOUD
115 + {
116 + fSemOwned = (WinRequestMutexSem(hmtx, ...) == NO_ERROR);
117 + if (fSemOwned)
118 + { ... // work on your protected data
119 + }
120 + }
121 + CATCH(excpt1) { } END_CATCH(); // always needed!
122 +
123 + if (fSemOwned) {
124 + // this gets executed always, even if an exception occured
125 + DosReleaseMutexSem(hmtx);
126 + fSemOwned = FALSE;
127 + }
128 + } // end of your_func
129 *
130 * This way your mutex semaphore gets released even if
131 * exceptions occur in your code. If you don't do this,
132 * threads waiting for that semaphore will be blocked
133 * forever when exceptions occur. As a result, depending
134 * on the thread, PM will hang, or the application will
135 * never terminate.
136 *
137 * <B>Customizing</B>
138 *
139 * Note: As opposed to versions < 0.9.0, this is now
140 * completely independent of XWorkplace. This file now
141 * contains "pure" exception handlers only.
142 *
143 * However, you can customize these exception handlers by
144 * calling excRegisterHooks. This is what XWorkplace does now.
145 * This should be done upon initialization of your application.
146 * If excRegisterHooks is not called, the following safe
147 * defaults are used:
148 * -- the trap log file is TRAP.LOG in the root
149 * directory of your boot drive.
150 *
151 * For details on the provided exception handlers, refer
152 * to excHandlerLoud and excHandlerQuiet.
153 *
154 * More useful debug information can be found in the "OS/2 Debugging
155 * Handbook", which is now available in INF format on the IBM
156 * DevCon site ("http://service2.boulder.ibm.com/devcon/").
157 * This book shows worked examples of how to unwind a stack dump.
158 *
159 * This file incorporates code from the following:
160 * -- Monte Copeland, IBM Boca Ration, Florida, USA (1993)
161 * -- Roman Stangl, from the Program Commander/2 sources
162 * (1997-98)
163 * -- Marc Fiammante, John Currier, Kim Rasmussen,
164 * Anthony Cruise (EXCEPT3.ZIP package for a generic
165 * exception handling DLL, available at Hobbes).
166 *
167 * If not explicitly stated otherwise, the code has been written
168 * by me, Ulrich M”ller.
169 *
170 * Note: Version numbering in this file relates to XWorkplace version
171 * numbering.
172 *
173 *@@header "helpers\except.h"
174 */
175
176/*
177 * This file Copyright (C) 1992-99 Ulrich M”ller,
178 * Monte Copeland,
179 * Roman Stangl,
180 * Kim Rasmussen,
181 * Marc Fiammante,
182 * John Currier,
183 * Anthony Cruise.
184 * This file is part of the XWorkplace source package.
185 * XWorkplace is free software; you can redistribute it and/or modify
186 * it under the terms of the GNU General Public License as published
187 * by the Free Software Foundation, in version 2 as it comes in the
188 * "COPYING" file of the XWorkplace main distribution.
189 * This program is distributed in the hope that it will be useful,
190 * but WITHOUT ANY WARRANTY; without even the implied warranty of
191 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
192 * GNU General Public License for more details.
193 */
194
195#define OS2EMX_PLAIN_CHAR
196 // this is needed for "os2emx.h"; if this is defined,
197 // emx will define PSZ as _signed_ char, otherwise
198 // as unsigned char
199
200#define INCL_DOSMODULEMGR
201#define INCL_DOSEXCEPTIONS
202#define INCL_DOSPROCESS
203#define INCL_DOSERRORS
204#include <os2.h>
205
206// C library headers
207#include <stdio.h> // needed for except.h
208#include <stdlib.h>
209#include <time.h>
210#include <string.h>
211#include <setjmp.h> // needed for except.h
212#include <assert.h> // needed for except.h
213
214#define DONT_REPLACE_MALLOC
215#include "setup.h" // code generation and debugging options
216
217// headers in /helpers
218#include "helpers\dosh.h" // Control Program helper routines
219#include "helpers\except.h" // exception handling
220#include "helpers\debug.h" // symbol/debug code analysis
221
222#pragma hdrstop
223
224/* ******************************************************************
225 * *
226 * Global variables *
227 * *
228 ********************************************************************/
229
230// hooks to be registered using excRegisterHooks
231PFNEXCOPENFILE G_pfnExcOpenFile = 0;
232PFNEXCHOOK G_pfnExcHook = 0;
233PFNEXCHOOKERROR G_pfnExcHookError = 0;
234// beep flag for excHandlerLoud
235BOOL G_fBeepOnException = TRUE;
236
237/*
238 *@@category: Helpers\Control program helpers\Exceptions/debugging
239 */
240
241/* ******************************************************************
242 * *
243 * Exception helper routines *
244 * *
245 ********************************************************************/
246
247/*
248 *@@ excDescribePage:
249 *
250 */
251
252VOID excDescribePage(FILE *file, ULONG ulCheck)
253{
254 APIRET arc;
255 ULONG ulCountPages = 1;
256 ULONG ulFlagsPage = 0;
257 arc = DosQueryMem((PVOID)ulCheck, &ulCountPages, &ulFlagsPage);
258
259 if (arc == NO_ERROR)
260 {
261 fprintf(file, "valid, flags: ");
262 if (ulFlagsPage & PAG_READ)
263 fprintf(file, "read ");
264 if (ulFlagsPage & PAG_WRITE)
265 fprintf(file, "write ");
266 if (ulFlagsPage & PAG_EXECUTE)
267 fprintf(file, "execute ");
268 if (ulFlagsPage & PAG_GUARD)
269 fprintf(file, "guard ");
270 if (ulFlagsPage & PAG_COMMIT)
271 fprintf(file, "committed ");
272 if (ulFlagsPage & PAG_SHARED)
273 fprintf(file, "shared ");
274 if (ulFlagsPage & PAG_FREE)
275 fprintf(file, "free ");
276 if (ulFlagsPage & PAG_BASE)
277 fprintf(file, "base ");
278 }
279 else if (arc == ERROR_INVALID_ADDRESS)
280 fprintf(file, "invalid");
281}
282
283/*
284 *@@ excPrintStackFrame:
285 * wrapper for dbgPrintStackFrame to format
286 * output stuff right.
287 *
288 *@@added V0.9.2 (2000-03-10) [umoeller]
289 */
290
291VOID excPrintStackFrame(FILE *file, // in: output log file
292 PSZ pszDescription, // in: description for stack frame (should be eight chars)
293 ULONG ulAddress) // in: address to debug
294{
295 APIRET arc = NO_ERROR;
296 HMODULE hmod1 = NULLHANDLE;
297 CHAR szMod1[2*CCHMAXPATH] = "unknown";
298 ULONG ulObject = 0,
299 ulOffset = 0;
300 fprintf(file,
301 " %-8s: %08X ",
302 pszDescription,
303 ulAddress);
304 arc = DosQueryModFromEIP(&hmod1,
305 &ulObject,
306 sizeof(szMod1), szMod1,
307 &ulOffset,
308 ulAddress);
309
310 if (arc != NO_ERROR)
311 {
312 // error:
313 fprintf(file,
314 " %-8s:%d Error: DosQueryModFromEIP returned %d\n",
315 szMod1,
316 ulObject,
317 arc);
318 }
319 else
320 {
321 CHAR szFullName[2*CCHMAXPATH];
322
323 fprintf(file,
324 " %-8s:%d ",
325 szMod1,
326 ulObject);
327
328 DosQueryModuleName(hmod1, sizeof(szFullName), szFullName);
329
330 dbgPrintStackFrame(file,
331 szFullName,
332 ulObject,
333 ulOffset);
334
335 fprintf(file, "\n");
336
337 // make a 'tick' sound to let the user know we're still alive
338 DosBeep(2000, 10);
339 }
340}
341
342/*
343 *@@ excDumpStackFrames:
344 * called from excExplainException to dump the
345 * thread's stack frames. This calls excPrintStackFrame
346 * for each stack frame found.
347 *
348 *@@added V0.9.4 (2000-06-15) [umoeller]
349 */
350
351VOID excDumpStackFrames(FILE *file, // in: logfile from fopen()
352 PTIB ptib,
353 PCONTEXTRECORD pContextRec) // in: excpt info
354{
355 PULONG pulStackWord = 0;
356
357 fprintf(file, "\n\nStack frames:\n Address Module:Object\n");
358
359 // first the trapping address itself
360 excPrintStackFrame(file,
361 "CS:EIP ",
362 pContextRec->ctx_RegEip);
363
364
365 pulStackWord = (PULONG)pContextRec->ctx_RegEbp;
366 /* if (pContextRec->ctx_RegEbp < pContextRec->ctx_RegEsp)
367 pulStackWord = (PULONG)(pContextRec->ctx_RegEbp & 0xFFFFFFF0);
368 else
369 pulStackWord = (PULONG)(pContextRec->ctx_RegEsp & 0xFFFFFFF0); */
370
371 while ( (pulStackWord != 0)
372 && (pulStackWord < (PULONG)ptib->tib_pstacklimit)
373 )
374 {
375 CHAR szAddress[20];
376
377 if (((ULONG)pulStackWord & 0x00000FFF) == 0x00000000)
378 {
379 // we're on a page boundary: check access
380 ULONG ulCountPages = 0x1000;
381 ULONG ulFlagsPage = 0;
382 APIRET arc = DosQueryMem((void *)pulStackWord,
383 &ulCountPages,
384 &ulFlagsPage);
385 if ( (arc != NO_ERROR)
386 || ( (arc == NO_ERROR)
387 && ( !( ((ulFlagsPage & (PAG_COMMIT|PAG_READ))
388 == (PAG_COMMIT|PAG_READ)
389 )
390 )
391 )
392 )
393 )
394 {
395 fprintf(file, "\n %08X: ", pulStackWord);
396 fprintf(file, "Page inaccessible");
397 pulStackWord += 0x1000;
398 continue; // for
399 }
400 }
401
402 sprintf(szAddress, "%08X",
403 pulStackWord);
404 excPrintStackFrame(file,
405 szAddress,
406 *(pulStackWord+1));
407 pulStackWord = (PULONG)*(pulStackWord);
408 } // end while
409}
410
411/*
412 *@@ excExplainException:
413 * used by the exception handlers below to write
414 * LOTS of information about the exception into a logfile.
415 *
416 * This calls excPrintStackFrame for each stack frame.
417 *
418 * If DEBUG_EXCPT_STACKDUMP is #define'd, this also dumps
419 * the stack completely, which isn't that useful.
420 *
421 *@@changed V0.9.0 [umoeller]: added support for application hook
422 *@@changed V0.9.0 (99-11-02) [umoeller]: added TID to dump
423 *@@changed V0.9.2 (2000-03-10) [umoeller]: now using excPrintStackFrame
424 *@@changed V0.9.3 (2000-05-03) [umoeller]: fixed crashes
425 */
426
427VOID excExplainException(FILE *file, // in: logfile from fopen()
428 PSZ pszHandlerName, // in: descriptive string
429 PEXCEPTIONREPORTRECORD pReportRec, // in: excpt info
430 PCONTEXTRECORD pContextRec) // in: excpt info
431{
432 PTIB ptib = NULL;
433 PPIB ppib = NULL;
434 HMODULE hMod1, hMod2;
435 CHAR szMod1[CCHMAXPATH] = "unknown",
436 szMod2[CCHMAXPATH] = "unknown";
437 ULONG ulObjNum,
438 ulOffset;
439 /* ulCountPages,
440 ulFlagsPage; */
441 // APIRET arc;
442 // PULONG pulStackWord;
443 // pulStackBegin;
444 ULONG ul;
445
446 ULONG ulOldPriority = 0x0100; // regular, delta 0
447
448 // raise this thread's priority, because this
449 // might take some time
450 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
451 if (ptib)
452 if (ptib->tib_ptib2)
453 {
454 ulOldPriority = ptib->tib_ptib2->tib2_ulpri;
455 DosSetPriority(PRTYS_THREAD,
456 PRTYC_REGULAR,
457 PRTYD_MAXIMUM,
458 0); // current thread
459 }
460
461 // make some noise
462 if (G_fBeepOnException)
463 {
464 DosBeep( 250, 30);
465 DosBeep( 500, 30);
466 DosBeep(1000, 30);
467 DosBeep(2000, 30);
468 DosBeep(4000, 30);
469 DosBeep(2000, 30);
470 DosBeep(1000, 30);
471 DosBeep( 500, 30);
472 DosBeep( 250, 30);
473 }
474
475 // generic exception info
476 fprintf(file,
477 "\n%s:\n Exception type: %08X\n Address: %08X\n Params: ",
478 pszHandlerName,
479 pReportRec->ExceptionNum,
480 pReportRec->ExceptionAddress);
481 for (ul = 0; ul < pReportRec->cParameters; ul++)
482 {
483 fprintf(file, "%08X ",
484 pReportRec->ExceptionInfo[ul]);
485 }
486
487 // now explain the exception in a bit more detail;
488 // depending on the exception, pReportRec->ExceptionInfo
489 // contains some useful data
490 switch (pReportRec->ExceptionNum)
491 {
492 case XCPT_ACCESS_VIOLATION:
493 {
494 fprintf(file, "\nAccess violation: ");
495 if (pReportRec->ExceptionInfo[0] & XCPT_READ_ACCESS)
496 fprintf(file, "Invalid read access from 0x%04X:%08X.\n",
497 pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]);
498 else if (pReportRec->ExceptionInfo[0] & XCPT_WRITE_ACCESS)
499 fprintf(file, "Invalid write access to 0x%04X:%08X.\n",
500 pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]);
501 else if (pReportRec->ExceptionInfo[0] & XCPT_SPACE_ACCESS)
502 fprintf(file, "Invalid space access at 0x%04X.\n",
503 pReportRec->ExceptionInfo[1]);
504 else if (pReportRec->ExceptionInfo[0] & XCPT_LIMIT_ACCESS)
505 fprintf(file, "Invalid limit access occurred.\n");
506 else if (pReportRec->ExceptionInfo[0] == XCPT_UNKNOWN_ACCESS)
507 fprintf(file, "unknown at 0x%04X:%08X\n",
508 pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]);
509 fprintf(file,
510 "Explanation: An attempt was made to access a memory object which does\n"
511 " not belong to the current process. Most probable causes\n"
512 " for this are that an invalid pointer was used, there was\n"
513 " confusion with administering memory or error conditions \n"
514 " were not properly checked for.\n");
515 break;
516 }
517
518 case XCPT_INTEGER_DIVIDE_BY_ZERO:
519 {
520 fprintf(file, "\nInteger division by zero.\n");
521 fprintf(file,
522 "Explanation: An attempt was made to divide an integer value by zero,\n"
523 " which is not defined.\n");
524 break;
525 }
526
527 case XCPT_ILLEGAL_INSTRUCTION:
528 {
529 fprintf(file, "\nIllegal instruction found.\n");
530 fprintf(file,
531 "Explanation: An attempt was made to execute an instruction that\n"
532 " is not defined on this machine's architecture.\n");
533 break;
534 }
535
536 case XCPT_PRIVILEGED_INSTRUCTION:
537 {
538 fprintf(file, "\nPrivileged instruction found.\n");
539 fprintf(file,
540 "Explanation: An attempt was made to execute an instruction that\n"
541 " is not permitted in the current machine mode or that\n"
542 " XFolder had no permission to execute.\n");
543 break;
544 }
545
546 case XCPT_INTEGER_OVERFLOW:
547 fprintf(file, "\nInteger overflow.\n");
548 fprintf(file,
549 "Explanation: An integer operation generated a carry-out of the most\n"
550 " significant bit. This is a sign of an attempt to store\n"
551 " a value which does not fit into an integer variable.\n");
552 }
553
554 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
555 {
556 /*
557 * process info:
558 *
559 */
560
561 if ((ptib) && (ppib)) // (99-11-01) [umoeller]
562 {
563 if (pContextRec->ContextFlags & CONTEXT_CONTROL)
564 {
565 // get the main module
566 hMod1 = ppib->pib_hmte;
567 DosQueryModuleName(hMod1,
568 sizeof(szMod1),
569 szMod1);
570
571 // get the trapping module
572 DosQueryModFromEIP(&hMod2,
573 &ulObjNum,
574 sizeof(szMod2),
575 szMod2,
576 &ulOffset,
577 pContextRec->ctx_RegEip);
578 DosQueryModuleName(hMod2,
579 sizeof(szMod2),
580 szMod2);
581 }
582
583 fprintf(file,
584 "\nProcess information:"
585 "\n Process ID: 0x%lX"
586 "\n Process module: 0x%lX (%s)"
587 "\n Trapping module: 0x%lX (%s)"
588 "\n Object: %d\n",
589 ppib->pib_ulpid,
590 hMod1, szMod1,
591 hMod2, szMod2,
592 ulObjNum);
593
594 fprintf(file,
595 "\nTrapping thread information:"
596 "\n Thread ID: 0x%lX (%d)"
597 "\n Priority: 0x%lX\n",
598 ptib->tib_ptib2->tib2_ultid, ptib->tib_ptib2->tib2_ultid,
599 ulOldPriority);
600 }
601 else
602 fprintf(file, "\nProcess information was not available.");
603
604 /*
605 * now call the hook, if one has been defined,
606 * so that the application can write additional
607 * information to the traplog (V0.9.0)
608 */
609
610 if (G_pfnExcHook)
611 {
612 (*G_pfnExcHook)(file, ptib);
613 }
614
615 // *** registers
616
617 fprintf(file, "\nRegisters:");
618 if (pContextRec->ContextFlags & CONTEXT_INTEGER)
619 {
620 // EAX
621 fprintf(file, "\n EAX = %08X ", pContextRec->ctx_RegEax);
622 excDescribePage(file, pContextRec->ctx_RegEax);
623 // EBX
624 fprintf(file, "\n EBX = %08X ", pContextRec->ctx_RegEbx);
625 excDescribePage(file, pContextRec->ctx_RegEbx);
626 // ECX
627 fprintf(file, "\n ECX = %08X ", pContextRec->ctx_RegEcx);
628 excDescribePage(file, pContextRec->ctx_RegEcx);
629 // EDX
630 fprintf(file, "\n EDX = %08X ", pContextRec->ctx_RegEdx);
631 excDescribePage(file, pContextRec->ctx_RegEdx);
632 // ESI
633 fprintf(file, "\n ESI = %08X ", pContextRec->ctx_RegEsi);
634 excDescribePage(file, pContextRec->ctx_RegEsi);
635 // EDI
636 fprintf(file, "\n EDI = %08X ", pContextRec->ctx_RegEdi);
637 excDescribePage(file, pContextRec->ctx_RegEdi);
638 fprintf(file, "\n");
639 }
640 else
641 fprintf(file, " not available\n");
642
643 if (pContextRec->ContextFlags & CONTEXT_CONTROL)
644 {
645
646 // *** instruction
647
648 fprintf(file, "Instruction pointer (where exception occured):\n CS:EIP = %04X:%08X ",
649 pContextRec->ctx_SegCs,
650 pContextRec->ctx_RegEip);
651 excDescribePage(file, pContextRec->ctx_RegEip);
652
653 // *** CPU flags
654
655 fprintf(file, "\n FLG = %08X", pContextRec->ctx_EFlags);
656
657 /*
658 * stack:
659 *
660 */
661
662 fprintf(file, "\nStack:\n Base: %08X\n Limit: %08X",
663 (ptib ? ptib->tib_pstack : 0),
664 (ptib ? ptib->tib_pstacklimit :0));
665 fprintf(file, "\n SS:ESP = %04X:%08X ",
666 pContextRec->ctx_SegSs, pContextRec->ctx_RegEsp);
667 excDescribePage(file, pContextRec->ctx_RegEsp);
668
669 fprintf(file, "\n EBP = %08X ", pContextRec->ctx_RegEbp);
670 excDescribePage(file, pContextRec->ctx_RegEbp);
671
672 /*
673 * stack dump:
674 */
675
676 if (ptib != 0)
677 excDumpStackFrames(file, ptib, pContextRec);
678 }
679 }
680 fprintf(file, "\n");
681
682 // reset old priority
683 DosSetPriority(PRTYS_THREAD,
684 (ulOldPriority & 0x0F00) >> 8,
685 (UCHAR)ulOldPriority,
686 0); // current thread
687}
688
689/* ******************************************************************
690 * *
691 * Exported routines *
692 * *
693 ********************************************************************/
694
695/*
696 *@@ excRegisterHooks:
697 * this registers hooks which get called for
698 * exception handlers. You can set any of the
699 * hooks to NULL for safe defaults (see top of
700 * except.c for details). You can set none,
701 * one, or both of the hooks, and you can call
702 * this function several times.
703 *
704 * Both hooks get called whenever an exception
705 * occurs, so there better be no bugs in these
706 * routines. ;-) They only get called from
707 * within excHandlerLoud (because excHandlerQuiet
708 * writes no trap logs).
709 *
710 * The hooks are as follows:
711 *
712 * pfnExcOpenFileNew gets called to open
713 * the trap log file. This must return a FILE*
714 * pointer from fopen(). If this is not defined,
715 * ?:\TRAP.LOG is used. Use this to specify a
716 * different file and have some notes written
717 * into it before the actual exception info.
718 *
719 * pfnExcHookNew gets called while the trap log
720 * is being written. At this point,
721 * the following info has been written into
722 * the trap log already:
723 * -- exception type/address block
724 * -- exception explanation
725 * -- process information
726 *
727 * _After_ the hook, the exception handler
728 * continues with the "Registers" information
729 * and stack dump/analysis.
730 *
731 * Use this hook to write additional application
732 * info into the trap log, such as the state
733 * of your threads.
734 *
735 * pfnExcHookError gets called when the TRY_* macros
736 * fail to install an exception handler (when
737 * DosSetExceptionHandler fails).
738 *
739 *@@added V0.9.0 [umoeller]
740 *@@changed V0.9.2 (2000-03-10) [umoeller]: pfnExcHookError added
741 */
742
743VOID excRegisterHooks(PFNEXCOPENFILE pfnExcOpenFileNew,
744 PFNEXCHOOK pfnExcHookNew,
745 PFNEXCHOOKERROR pfnExcHookError,
746 BOOL fBeepOnExceptionNew)
747{
748 // adjust the global variables
749 G_pfnExcOpenFile = pfnExcOpenFileNew;
750 G_pfnExcHook = pfnExcHookNew;
751 G_pfnExcHookError = pfnExcHookError;
752 G_fBeepOnException = fBeepOnExceptionNew;
753}
754
755/*
756 *@@ excHandlerLoud:
757 * this is the "sophisticated" exception handler;
758 * which gives forth a loud sequence of beeps thru the
759 * speaker, writes a trap log and then returns back
760 * to the thread to continue execution, i.e. the
761 * default OS/2 exception handler will never get
762 * called. This requires a setjmp() call on
763 * EXCEPTIONREGISTRATIONRECORD2.jmpThread before
764 * being installed.
765 *
766 * This is best registered thru the TRY_LOUD macro
767 * (new with V0.84, described in except.c), which
768 * does the necessary setup.
769 *
770 * Depending on the type of exception, the following
771 * happens:
772 *
773 * <B>a) "real" exceptions</B>
774 *
775 * -- XCPT_ACCESS_VIOLATION (traps 0x0d, 0x0e)
776 * -- XCPT_INTEGER_DIVIDE_BY_ZERO (trap 0)
777 * -- XCPT_ILLEGAL_INSTRUCTION (trap 6)
778 * -- XCPT_PRIVILEGED_INSTRUCTION
779 * -- XCPT_INTEGER_OVERFLOW (trap 4)
780 *
781 * For these exceptions, we call the functions in debug.c
782 * to try to find debug code or SYM file information about
783 * what source code corresponds to the error.
784 *
785 * Note that to get meaningful debugging information
786 * in this handler's traplog, you need the following:
787 * a) have a MAP file created at link time (/MAP)
788 * b) convert the MAP to a SYM file using MAPSYM
789 * c) put the SYM file in the same directory of
790 * the executable. This must have the same
791 * filestem as the executable.
792 *
793 * See the "Control Programming Guide and Reference"
794 * for details.
795 * All other exceptions are passed to the next handler
796 * in the exception handler chain. This might be the
797 * C/C++ compiler handler or the default OS/2 handler,
798 * which will probably terminate the process.
799 *
800 * <B>b) thread kills</B>
801 *
802 * -- XCPT_PROCESS_TERMINATE
803 * -- XCPT_ASYNC_PROCESS_TERMINATE:
804 *
805 * If EXCEPTIONREGISTRATIONRECORD2.pfnOnKill is != NULL,
806 * that function gets called to allow for thread cleanup
807 * before the thread really terminates. This should be
808 * used for releasing mutex semaphores.
809 *
810 *@@changed V0.9.0 [umoeller]: added support for thread termination
811 *@@changed V0.9.2 (2000-03-10) [umoeller]: switched date format to ISO
812 */
813
814ULONG _System excHandlerLoud(PEXCEPTIONREPORTRECORD pReportRec,
815 PEXCEPTIONREGISTRATIONRECORD2 pRegRec2,
816 PCONTEXTRECORD pContextRec,
817 PVOID pv)
818{
819 /* From the VAC++3 docs:
820 * "The first thing an exception handler should do is check the
821 * exception flags. If EH_EXIT_UNWIND is set, meaning
822 * the thread is ending, the handler tells the operating system
823 * to pass the exception to the next exception handler. It does the
824 * same if the EH_UNWINDING flag is set, the flag that indicates
825 * this exception handler is being removed.
826 * The EH_NESTED_CALL flag indicates whether the exception
827 * occurred within an exception handler. If the handler does
828 * not check this flag, recursive exceptions could occur until
829 * there is no stack remaining."
830 * So for all these conditions, we exit immediately.
831 */
832
833 if (pReportRec->fHandlerFlags & EH_EXIT_UNWIND)
834 return (XCPT_CONTINUE_SEARCH);
835 if (pReportRec->fHandlerFlags & EH_UNWINDING)
836 return (XCPT_CONTINUE_SEARCH);
837 if (pReportRec->fHandlerFlags & EH_NESTED_CALL)
838 return (XCPT_CONTINUE_SEARCH);
839
840 switch (pReportRec->ExceptionNum)
841 {
842 case XCPT_PROCESS_TERMINATE:
843 case XCPT_ASYNC_PROCESS_TERMINATE:
844 // thread terminated:
845 // if the handler has been registered to catch
846 // these exceptions, continue;
847 if (pRegRec2->pfnOnKill)
848 // call the "OnKill" function
849 pRegRec2->pfnOnKill();
850 // get outta here, which will kill the thread
851 break;
852
853 case XCPT_ACCESS_VIOLATION:
854 case XCPT_INTEGER_DIVIDE_BY_ZERO:
855 case XCPT_ILLEGAL_INSTRUCTION:
856 case XCPT_PRIVILEGED_INSTRUCTION:
857 case XCPT_INTEGER_OVERFLOW:
858 {
859 // "real" exceptions:
860 FILE *file;
861
862 // open traplog file;
863 if (G_pfnExcOpenFile)
864 // hook defined for this: call it
865 file = (*G_pfnExcOpenFile)();
866 else
867 {
868 CHAR szFileName[100];
869 // no hook defined: open some
870 // default traplog file in root directory of
871 // boot drive
872 sprintf(szFileName, "%c:\\trap.log", doshQueryBootDrive());
873 file = fopen(szFileName, "a");
874
875 if (file)
876 {
877 DATETIME DT;
878 DosGetDateTime(&DT);
879 fprintf(file,
880 "\nTrap message -- Date: %04d-%02d-%02d, Time: %02d:%02d:%02d\n",
881 DT.year, DT.month, DT.day,
882 DT.hours, DT.minutes, DT.seconds);
883 fprintf(file, "------------------------------------------------\n");
884
885 }
886 }
887
888 // write error log
889 excExplainException(file,
890 "excHandlerLoud",
891 pReportRec,
892 pContextRec);
893 fclose(file);
894
895 // jump back to failing routine
896 /* DosSetPriority(PRTYS_THREAD,
897 PRTYC_REGULAR,
898 0, // delta
899 0); // current thread
900 */
901 longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum);
902 break; }
903 }
904
905 // not handled
906 return (XCPT_CONTINUE_SEARCH);
907}
908
909/*
910 *@@ excHandlerQuiet:
911 * "quiet" xcpt handler, which simply suppresses exceptions;
912 * this is useful for certain error-prone functions, where
913 * exceptions are likely to appear, for example used by
914 * cmnCheckObject to implement a fail-safe SOM object check.
915 *
916 * This does _not_ write an error log and makes _no_ sound.
917 * This simply jumps back to the trapping thread or
918 * calls EXCEPTIONREGISTRATIONRECORD2.pfnOnKill.
919 *
920 * Other than that, this behaves like excHandlerLoud.
921 *
922 * This is best registered thru the TRY_QUIET macro
923 * (new with V0.84, described in except.c), which
924 * does the necessary setup.
925 *
926 *@@changed V0.9.0 [umoeller]: added support for thread termination
927 */
928
929ULONG _System excHandlerQuiet(PEXCEPTIONREPORTRECORD pReportRec,
930 PEXCEPTIONREGISTRATIONRECORD2 pRegRec2,
931 PCONTEXTRECORD pContextRec,
932 PVOID pv)
933{
934 if (pReportRec->fHandlerFlags & EH_EXIT_UNWIND)
935 return (XCPT_CONTINUE_SEARCH);
936 if (pReportRec->fHandlerFlags & EH_UNWINDING)
937 return (XCPT_CONTINUE_SEARCH);
938 if (pReportRec->fHandlerFlags & EH_NESTED_CALL)
939 return (XCPT_CONTINUE_SEARCH);
940
941 switch (pReportRec->ExceptionNum)
942 {
943 case XCPT_PROCESS_TERMINATE:
944 case XCPT_ASYNC_PROCESS_TERMINATE:
945 // thread terminated:
946 // if the handler has been registered to catch
947 // these exceptions, continue;
948 if (pRegRec2->pfnOnKill)
949 // call the "OnKill" function
950 pRegRec2->pfnOnKill();
951 // get outta here, which will kill the thread
952 break;
953
954 case XCPT_ACCESS_VIOLATION:
955 case XCPT_INTEGER_DIVIDE_BY_ZERO:
956 case XCPT_ILLEGAL_INSTRUCTION:
957 case XCPT_PRIVILEGED_INSTRUCTION:
958 case XCPT_INTEGER_OVERFLOW:
959 // write excpt explanation only if the
960 // resp. debugging #define is set (common.h)
961 #ifdef DEBUG_WRITEQUIETEXCPT
962 {
963 FILE *file = excOpenTraplogFile();
964 excExplainException(file,
965 "excHandlerQuiet",
966 pReportRec,
967 pContextRec);
968 fclose(file);
969 }
970 #endif
971
972 // jump back to failing routine
973 longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum);
974 break;
975
976 default:
977 break;
978 }
979
980 return (XCPT_CONTINUE_SEARCH);
981}
982
983
Note: See TracBrowser for help on using the repository browser.