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