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