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

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

Updates for V0.9.6.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 37.1 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: %08lX ",
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:%lu Error: DosQueryModFromEIP returned %lu\n",
315 szMod1,
316 ulObject,
317 arc);
318 }
319 else
320 {
321 CHAR szFullName[2*CCHMAXPATH];
322
323 fprintf(file,
324 " %-8s:%lu ",
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 %08lX: ", (ULONG)pulStackWord);
396 fprintf(file, "Page inaccessible");
397 pulStackWord += 0x1000;
398 continue; // for
399 }
400 }
401
402 sprintf(szAddress, "%08lX",
403 (ULONG)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 *@@changed V0.9.0 [umoeller]: added support for application hook
419 *@@changed V0.9.0 (99-11-02) [umoeller]: added TID to dump
420 *@@changed V0.9.2 (2000-03-10) [umoeller]: now using excPrintStackFrame
421 *@@changed V0.9.3 (2000-05-03) [umoeller]: fixed crashes
422 *@@changed V0.9.6 (2000-11-06) [umoeller]: added more register dumps
423 */
424
425VOID excExplainException(FILE *file, // in: logfile from fopen()
426 PSZ pszHandlerName, // in: descriptive string
427 PEXCEPTIONREPORTRECORD pReportRec, // in: excpt info
428 PCONTEXTRECORD pContextRec) // in: excpt info
429{
430 PTIB ptib = NULL;
431 PPIB ppib = NULL;
432 HMODULE hMod1, hMod2;
433 CHAR szMod1[CCHMAXPATH] = "unknown",
434 szMod2[CCHMAXPATH] = "unknown";
435 ULONG ulObjNum,
436 ulOffset;
437 /* ulCountPages,
438 ulFlagsPage; */
439 // APIRET arc;
440 // PULONG pulStackWord;
441 // pulStackBegin;
442 ULONG ul;
443
444 ULONG ulOldPriority = 0x0100; // regular, delta 0
445
446 // raise this thread's priority, because this
447 // might take some time
448 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
449 if (ptib)
450 if (ptib->tib_ptib2)
451 {
452 ulOldPriority = ptib->tib_ptib2->tib2_ulpri;
453 DosSetPriority(PRTYS_THREAD,
454 PRTYC_REGULAR,
455 PRTYD_MAXIMUM,
456 0); // current thread
457 }
458
459 // make some noise
460 if (G_fBeepOnException)
461 {
462 DosBeep( 250, 30);
463 DosBeep( 500, 30);
464 DosBeep(1000, 30);
465 DosBeep(2000, 30);
466 DosBeep(4000, 30);
467 DosBeep(2000, 30);
468 DosBeep(1000, 30);
469 DosBeep( 500, 30);
470 DosBeep( 250, 30);
471 }
472
473 // generic exception info
474 fprintf(file,
475 "\n%s:\n Exception type: %08lX\n Address: %08lX\n Params: ",
476 pszHandlerName,
477 pReportRec->ExceptionNum,
478 (ULONG)pReportRec->ExceptionAddress);
479 for (ul = 0; ul < pReportRec->cParameters; ul++)
480 {
481 fprintf(file, "%08lX ",
482 pReportRec->ExceptionInfo[ul]);
483 }
484
485 // now explain the exception in a bit more detail;
486 // depending on the exception, pReportRec->ExceptionInfo
487 // contains some useful data
488 switch (pReportRec->ExceptionNum)
489 {
490 case XCPT_ACCESS_VIOLATION:
491 {
492 fprintf(file, "\nAccess violation: ");
493 if (pReportRec->ExceptionInfo[0] & XCPT_READ_ACCESS)
494 fprintf(file, "Invalid read access from 0x%04lX:%08lX.\n",
495 pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]);
496 else if (pReportRec->ExceptionInfo[0] & XCPT_WRITE_ACCESS)
497 fprintf(file, "Invalid write access to 0x%04lX:%08lX.\n",
498 pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]);
499 else if (pReportRec->ExceptionInfo[0] & XCPT_SPACE_ACCESS)
500 fprintf(file, "Invalid space access at 0x%04lX.\n",
501 pReportRec->ExceptionInfo[1]);
502 else if (pReportRec->ExceptionInfo[0] & XCPT_LIMIT_ACCESS)
503 fprintf(file, "Invalid limit access occurred.\n");
504 else if (pReportRec->ExceptionInfo[0] == XCPT_UNKNOWN_ACCESS)
505 fprintf(file, "unknown at 0x%04lX:%08lX\n",
506 pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]);
507 fprintf(file,
508 "Explanation: An attempt was made to access a memory object which does\n"
509 " not belong to the current process. Most probable causes\n"
510 " for this are that an invalid pointer was used, there was\n"
511 " confusion with administering memory or error conditions \n"
512 " were not properly checked for.\n");
513 break;
514 }
515
516 case XCPT_INTEGER_DIVIDE_BY_ZERO:
517 {
518 fprintf(file, "\nInteger division by zero.\n");
519 fprintf(file,
520 "Explanation: An attempt was made to divide an integer value by zero,\n"
521 " which is not defined.\n");
522 break;
523 }
524
525 case XCPT_ILLEGAL_INSTRUCTION:
526 {
527 fprintf(file, "\nIllegal instruction found.\n");
528 fprintf(file,
529 "Explanation: An attempt was made to execute an instruction that\n"
530 " is not defined on this machine's architecture.\n");
531 break;
532 }
533
534 case XCPT_PRIVILEGED_INSTRUCTION:
535 {
536 fprintf(file, "\nPrivileged instruction found.\n");
537 fprintf(file,
538 "Explanation: An attempt was made to execute an instruction that\n"
539 " is not permitted in the current machine mode or that\n"
540 " XFolder had no permission to execute.\n");
541 break;
542 }
543
544 case XCPT_INTEGER_OVERFLOW:
545 fprintf(file, "\nInteger overflow.\n");
546 fprintf(file,
547 "Explanation: An integer operation generated a carry-out of the most\n"
548 " significant bit. This is a sign of an attempt to store\n"
549 " a value which does not fit into an integer variable.\n");
550 }
551
552 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
553 {
554 /*
555 * process info:
556 *
557 */
558
559 if ((ptib) && (ppib)) // (99-11-01) [umoeller]
560 {
561 if (pContextRec->ContextFlags & CONTEXT_CONTROL)
562 {
563 // get the main module
564 hMod1 = ppib->pib_hmte;
565 DosQueryModuleName(hMod1,
566 sizeof(szMod1),
567 szMod1);
568
569 // get the trapping module
570 DosQueryModFromEIP(&hMod2,
571 &ulObjNum,
572 sizeof(szMod2),
573 szMod2,
574 &ulOffset,
575 pContextRec->ctx_RegEip);
576 DosQueryModuleName(hMod2,
577 sizeof(szMod2),
578 szMod2);
579 }
580
581 fprintf(file,
582 "\nProcess information:"
583 "\n Process ID: 0x%lX"
584 "\n Process module: 0x%lX (%s)"
585 "\n Trapping module: 0x%lX (%s)"
586 "\n Object: %lu\n",
587 ppib->pib_ulpid,
588 hMod1, szMod1,
589 hMod2, szMod2,
590 ulObjNum);
591
592 fprintf(file,
593 "\nTrapping thread information:"
594 "\n Thread ID: 0x%lX (%lu)"
595 "\n Priority: 0x%lX\n",
596 ptib->tib_ptib2->tib2_ultid, ptib->tib_ptib2->tib2_ultid,
597 ulOldPriority);
598 }
599 else
600 fprintf(file, "\nProcess information was not available.");
601
602 /*
603 * now call the hook, if one has been defined,
604 * so that the application can write additional
605 * information to the traplog (V0.9.0)
606 */
607
608 if (G_pfnExcHook)
609 {
610 (*G_pfnExcHook)(file, ptib);
611 }
612
613 // *** registers
614
615 fprintf(file, "\nRegisters:");
616 if (pContextRec->ContextFlags & CONTEXT_INTEGER)
617 {
618 // DS the following 4 added V0.9.6 (2000-11-06) [umoeller]
619 fprintf(file, "\n DS = %08lX ", pContextRec->ctx_SegDs);
620 excDescribePage(file, pContextRec->ctx_SegDs);
621 // ES
622 fprintf(file, "\n ES = %08lX ", pContextRec->ctx_SegEs);
623 excDescribePage(file, pContextRec->ctx_SegEs);
624 // FS
625 fprintf(file, "\n FS = %08lX ", pContextRec->ctx_SegFs);
626 excDescribePage(file, pContextRec->ctx_SegFs);
627 // GS
628 fprintf(file, "\n GS = %08lX ", pContextRec->ctx_SegGs);
629 excDescribePage(file, pContextRec->ctx_SegGs);
630
631 // EAX
632 fprintf(file, "\n EAX = %08lX ", pContextRec->ctx_RegEax);
633 excDescribePage(file, pContextRec->ctx_RegEax);
634 // EBX
635 fprintf(file, "\n EBX = %08lX ", pContextRec->ctx_RegEbx);
636 excDescribePage(file, pContextRec->ctx_RegEbx);
637 // ECX
638 fprintf(file, "\n ECX = %08lX ", pContextRec->ctx_RegEcx);
639 excDescribePage(file, pContextRec->ctx_RegEcx);
640 // EDX
641 fprintf(file, "\n EDX = %08lX ", pContextRec->ctx_RegEdx);
642 excDescribePage(file, pContextRec->ctx_RegEdx);
643 // ESI
644 fprintf(file, "\n ESI = %08lX ", pContextRec->ctx_RegEsi);
645 excDescribePage(file, pContextRec->ctx_RegEsi);
646 // EDI
647 fprintf(file, "\n EDI = %08lX ", pContextRec->ctx_RegEdi);
648 excDescribePage(file, pContextRec->ctx_RegEdi);
649 fprintf(file, "\n");
650 }
651 else
652 fprintf(file, " not available\n");
653
654 if (pContextRec->ContextFlags & CONTEXT_CONTROL)
655 {
656
657 // *** instruction
658
659 fprintf(file, "Instruction pointer (where exception occured):\n CS:EIP = %04lX:%08lX ",
660 pContextRec->ctx_SegCs,
661 pContextRec->ctx_RegEip);
662 excDescribePage(file, pContextRec->ctx_RegEip);
663
664 // *** CPU flags
665
666 fprintf(file, "\n EFLAGS = %08lX", pContextRec->ctx_EFlags);
667
668 /*
669 * stack:
670 *
671 */
672
673 fprintf(file, "\nStack:\n Base: %08lX\n Limit: %08lX",
674 (ULONG)(ptib ? ptib->tib_pstack : 0),
675 (ULONG)(ptib ? ptib->tib_pstacklimit : 0));
676 fprintf(file, "\n SS:ESP = %04lX:%08lX ",
677 pContextRec->ctx_SegSs,
678 pContextRec->ctx_RegEsp);
679 excDescribePage(file, pContextRec->ctx_RegEsp);
680
681 fprintf(file, "\n EBP = %08lX ", pContextRec->ctx_RegEbp);
682 excDescribePage(file, pContextRec->ctx_RegEbp);
683
684 /*
685 * stack dump:
686 */
687
688 if (ptib != 0)
689 excDumpStackFrames(file, ptib, pContextRec);
690 }
691 }
692 fprintf(file, "\n");
693
694 // reset old priority
695 DosSetPriority(PRTYS_THREAD,
696 (ulOldPriority & 0x0F00) >> 8,
697 (UCHAR)ulOldPriority,
698 0); // current thread
699}
700
701/* ******************************************************************
702 * *
703 * Exported routines *
704 * *
705 ********************************************************************/
706
707/*
708 *@@ excRegisterHooks:
709 * this registers hooks which get called for
710 * exception handlers. You can set any of the
711 * hooks to NULL for safe defaults (see top of
712 * except.c for details). You can set none,
713 * one, or both of the hooks, and you can call
714 * this function several times.
715 *
716 * Both hooks get called whenever an exception
717 * occurs, so there better be no bugs in these
718 * routines. ;-) They only get called from
719 * within excHandlerLoud (because excHandlerQuiet
720 * writes no trap logs).
721 *
722 * The hooks are as follows:
723 *
724 * pfnExcOpenFileNew gets called to open
725 * the trap log file. This must return a FILE*
726 * pointer from fopen(). If this is not defined,
727 * ?:\TRAP.LOG is used. Use this to specify a
728 * different file and have some notes written
729 * into it before the actual exception info.
730 *
731 * pfnExcHookNew gets called while the trap log
732 * is being written. At this point,
733 * the following info has been written into
734 * the trap log already:
735 * -- exception type/address block
736 * -- exception explanation
737 * -- process information
738 *
739 * _After_ the hook, the exception handler
740 * continues with the "Registers" information
741 * and stack dump/analysis.
742 *
743 * Use this hook to write additional application
744 * info into the trap log, such as the state
745 * of your threads.
746 *
747 * pfnExcHookError gets called when the TRY_* macros
748 * fail to install an exception handler (when
749 * DosSetExceptionHandler fails).
750 *
751 *@@added V0.9.0 [umoeller]
752 *@@changed V0.9.2 (2000-03-10) [umoeller]: pfnExcHookError added
753 */
754
755VOID excRegisterHooks(PFNEXCOPENFILE pfnExcOpenFileNew,
756 PFNEXCHOOK pfnExcHookNew,
757 PFNEXCHOOKERROR pfnExcHookError,
758 BOOL fBeepOnExceptionNew)
759{
760 // adjust the global variables
761 G_pfnExcOpenFile = pfnExcOpenFileNew;
762 G_pfnExcHook = pfnExcHookNew;
763 G_pfnExcHookError = pfnExcHookError;
764 G_fBeepOnException = fBeepOnExceptionNew;
765}
766
767/*
768 *@@ excHandlerLoud:
769 * this is the "sophisticated" exception handler;
770 * which gives forth a loud sequence of beeps thru the
771 * speaker, writes a trap log and then returns back
772 * to the thread to continue execution, i.e. the
773 * default OS/2 exception handler will never get
774 * called. This requires a setjmp() call on
775 * EXCEPTIONREGISTRATIONRECORD2.jmpThread before
776 * being installed.
777 *
778 * This is best registered thru the TRY_LOUD macro
779 * (new with V0.84, described in except.c), which
780 * does the necessary setup.
781 *
782 * Depending on the type of exception, the following
783 * happens:
784 *
785 * <B>a) "real" exceptions</B>
786 *
787 * -- XCPT_ACCESS_VIOLATION (traps 0x0d, 0x0e)
788 * -- XCPT_INTEGER_DIVIDE_BY_ZERO (trap 0)
789 * -- XCPT_ILLEGAL_INSTRUCTION (trap 6)
790 * -- XCPT_PRIVILEGED_INSTRUCTION
791 * -- XCPT_INTEGER_OVERFLOW (trap 4)
792 *
793 * For these exceptions, we call the functions in debug.c
794 * to try to find debug code or SYM file information about
795 * what source code corresponds to the error.
796 *
797 * Note that to get meaningful debugging information
798 * in this handler's traplog, you need the following:
799 * a) have a MAP file created at link time (/MAP)
800 * b) convert the MAP to a SYM file using MAPSYM
801 * c) put the SYM file in the same directory of
802 * the executable. This must have the same
803 * filestem as the executable.
804 *
805 * See the "Control Programming Guide and Reference"
806 * for details.
807 * All other exceptions are passed to the next handler
808 * in the exception handler chain. This might be the
809 * C/C++ compiler handler or the default OS/2 handler,
810 * which will probably terminate the process.
811 *
812 * <B>b) thread kills</B>
813 *
814 * -- XCPT_PROCESS_TERMINATE
815 * -- XCPT_ASYNC_PROCESS_TERMINATE:
816 *
817 * If EXCEPTIONREGISTRATIONRECORD2.pfnOnKill is != NULL,
818 * that function gets called to allow for thread cleanup
819 * before the thread really terminates. This should be
820 * used for releasing mutex semaphores.
821 *
822 *@@changed V0.9.0 [umoeller]: added support for thread termination
823 *@@changed V0.9.2 (2000-03-10) [umoeller]: switched date format to ISO
824 */
825
826ULONG _System excHandlerLoud(PEXCEPTIONREPORTRECORD pReportRec,
827 PEXCEPTIONREGISTRATIONRECORD2 pRegRec2,
828 PCONTEXTRECORD pContextRec,
829 PVOID pv)
830{
831 /* From the VAC++3 docs:
832 * "The first thing an exception handler should do is check the
833 * exception flags. If EH_EXIT_UNWIND is set, meaning
834 * the thread is ending, the handler tells the operating system
835 * to pass the exception to the next exception handler. It does the
836 * same if the EH_UNWINDING flag is set, the flag that indicates
837 * this exception handler is being removed.
838 * The EH_NESTED_CALL flag indicates whether the exception
839 * occurred within an exception handler. If the handler does
840 * not check this flag, recursive exceptions could occur until
841 * there is no stack remaining."
842 * So for all these conditions, we exit immediately.
843 */
844
845 if (pReportRec->fHandlerFlags & EH_EXIT_UNWIND)
846 return (XCPT_CONTINUE_SEARCH);
847 if (pReportRec->fHandlerFlags & EH_UNWINDING)
848 return (XCPT_CONTINUE_SEARCH);
849 if (pReportRec->fHandlerFlags & EH_NESTED_CALL)
850 return (XCPT_CONTINUE_SEARCH);
851
852 switch (pReportRec->ExceptionNum)
853 {
854 case XCPT_PROCESS_TERMINATE:
855 case XCPT_ASYNC_PROCESS_TERMINATE:
856 // thread terminated:
857 // if the handler has been registered to catch
858 // these exceptions, continue;
859 if (pRegRec2->pfnOnKill)
860 // call the "OnKill" function
861 pRegRec2->pfnOnKill();
862 // get outta here, which will kill the thread
863 break;
864
865 case XCPT_ACCESS_VIOLATION:
866 case XCPT_INTEGER_DIVIDE_BY_ZERO:
867 case XCPT_ILLEGAL_INSTRUCTION:
868 case XCPT_PRIVILEGED_INSTRUCTION:
869 case XCPT_INTEGER_OVERFLOW:
870 {
871 // "real" exceptions:
872 FILE *file;
873
874 // open traplog file;
875 if (G_pfnExcOpenFile)
876 // hook defined for this: call it
877 file = (*G_pfnExcOpenFile)();
878 else
879 {
880 CHAR szFileName[100];
881 // no hook defined: open some
882 // default traplog file in root directory of
883 // boot drive
884 sprintf(szFileName, "%c:\\trap.log", doshQueryBootDrive());
885 file = fopen(szFileName, "a");
886
887 if (file)
888 {
889 DATETIME DT;
890 DosGetDateTime(&DT);
891 fprintf(file,
892 "\nTrap message -- Date: %04d-%02d-%02d, Time: %02d:%02d:%02d\n",
893 DT.year, DT.month, DT.day,
894 DT.hours, DT.minutes, DT.seconds);
895 fprintf(file, "------------------------------------------------\n");
896
897 }
898 }
899
900 // write error log
901 excExplainException(file,
902 "excHandlerLoud",
903 pReportRec,
904 pContextRec);
905 fclose(file);
906
907 // jump back to failing routine
908 /* DosSetPriority(PRTYS_THREAD,
909 PRTYC_REGULAR,
910 0, // delta
911 0); // current thread
912 */
913 longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum);
914 break; }
915 }
916
917 // not handled
918 return (XCPT_CONTINUE_SEARCH);
919}
920
921/*
922 *@@ excHandlerQuiet:
923 * "quiet" xcpt handler, which simply suppresses exceptions;
924 * this is useful for certain error-prone functions, where
925 * exceptions are likely to appear, for example used by
926 * cmnCheckObject to implement a fail-safe SOM object check.
927 *
928 * This does _not_ write an error log and makes _no_ sound.
929 * This simply jumps back to the trapping thread or
930 * calls EXCEPTIONREGISTRATIONRECORD2.pfnOnKill.
931 *
932 * Other than that, this behaves like excHandlerLoud.
933 *
934 * This is best registered thru the TRY_QUIET macro
935 * (new with V0.84, described in except.c), which
936 * does the necessary setup.
937 *
938 *@@changed V0.9.0 [umoeller]: added support for thread termination
939 */
940
941ULONG _System excHandlerQuiet(PEXCEPTIONREPORTRECORD pReportRec,
942 PEXCEPTIONREGISTRATIONRECORD2 pRegRec2,
943 PCONTEXTRECORD pContextRec,
944 PVOID pv)
945{
946 if (pReportRec->fHandlerFlags & EH_EXIT_UNWIND)
947 return (XCPT_CONTINUE_SEARCH);
948 if (pReportRec->fHandlerFlags & EH_UNWINDING)
949 return (XCPT_CONTINUE_SEARCH);
950 if (pReportRec->fHandlerFlags & EH_NESTED_CALL)
951 return (XCPT_CONTINUE_SEARCH);
952
953 switch (pReportRec->ExceptionNum)
954 {
955 case XCPT_PROCESS_TERMINATE:
956 case XCPT_ASYNC_PROCESS_TERMINATE:
957 // thread terminated:
958 // if the handler has been registered to catch
959 // these exceptions, continue;
960 if (pRegRec2->pfnOnKill)
961 // call the "OnKill" function
962 pRegRec2->pfnOnKill();
963 // get outta here, which will kill the thread
964 break;
965
966 case XCPT_ACCESS_VIOLATION:
967 case XCPT_INTEGER_DIVIDE_BY_ZERO:
968 case XCPT_ILLEGAL_INSTRUCTION:
969 case XCPT_PRIVILEGED_INSTRUCTION:
970 case XCPT_INTEGER_OVERFLOW:
971 // write excpt explanation only if the
972 // resp. debugging #define is set (common.h)
973 #ifdef DEBUG_WRITEQUIETEXCPT
974 {
975 FILE *file = excOpenTraplogFile();
976 excExplainException(file,
977 "excHandlerQuiet",
978 pReportRec,
979 pContextRec);
980 fclose(file);
981 }
982 #endif
983
984 // jump back to failing routine
985 longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum);
986 break;
987
988 default:
989 break;
990 }
991
992 return (XCPT_CONTINUE_SEARCH);
993}
994
995
Note: See TracBrowser for help on using the repository browser.