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

Last change on this file since 159 was 158, checked in by umoeller, 23 years ago

Misc fixes.

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