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

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

misc. changes

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