diff options
Diffstat (limited to 'sal/osl/os2/except.c')
-rw-r--r-- | sal/osl/os2/except.c | 1059 |
1 files changed, 1059 insertions, 0 deletions
diff --git a/sal/osl/os2/except.c b/sal/osl/os2/except.c new file mode 100644 index 000000000000..29962889fb01 --- /dev/null +++ b/sal/osl/os2/except.c @@ -0,0 +1,1059 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +/* + *@@sourcefile except.c: + * this file contains powerful exception handlers. + * except.h also defines easy-to-use macros for them. + * + * Usage: All OS/2 programs, PM or text mode. + * + * <B>Introduction</B> + * + * OS/2 exception handlers are a mess to program and, + * if installed wrongly, almost impossible to debug. + * The problem is that for any program that does a bit + * more than showing a message box, using exception + * handlers is a must to avoid system hangs. This + * especially applies to multi-thread programs using + * mutex semaphores (more on that below). The functions + * and macros in here are designed to make that more + * simple. + * + * The macros in except.h automatically insert code for + * properly registering and deregistering the handlers + * in except.c. You should ALWAYS use these macros + * instead of directly registering the handlers to avoid + * accidentally forgetting to deregister them. If you + * forget to deregister an exception handler, this can + * lead to really strange errors (crashes, hangs) which + * are nearly impossible to debug because the thread's + * stack probably got completely messed up. + * + * The general idea of these macros is to define + * TRY / CATCH blocks similar to C++. If an exception + * occurs in the TRY block, execution is transferred to + * the CATCH block. (This works in both C and C++, by the + * way.) + * + * The "OnKill" function that was added with V0.9.0 has + * been removed again with V0.9.7. + * + * The general usage is like this: + * + + int your_protected_func(int ...) + + { + + TRY_LOUD(excptid) // or: TRY_QUIET(excptid) + + { + + char *p = NULL; + + + + .... // the stuff in here is protected by + + // the excHandlerLoud or excHandlerQuiet + + // exception handler + + *p = "A"; + + } + + CATCH(excptid) + + { + + .... // exception occured: react here + + } END_CATCH(); // always needed! + + } // end of your_func + * + * TRY_LOUD is for installing excHandlerLoud. + * TRY_QUIET is for installing excHandlerQuiet. + * CATCH / END_CATCH are the same for the two. This + * is where the exception handler jumps to if an + * exception occurs. + * The CATCH block is _required_ even if you do nothing + * in there, because the CATCH() macro will deregister + * the handler. + * + * "excptid" can be any C identifier which is not used in + * your current variable scope, e.g. "excpt1". This + * is used for creating an EXCEPTSTRUCT variable of + * that name on the stack. The "excptid"'s in TRY_* and + * CATCH must match, since this is where the macros + * store the exception handler data. + * + * These macros may be nested if you use different + * "excptid"'s for sub-macros. + * + * Inside the TRY and CATCH blocks, you must not use + * "goto" (to a location outside the block) or "return", + * because this will not deregister the handler. + * + * Keep in mind that all the code in the TRY_* block is + * protected by the handler, including all functions that + * get called. So if you enclose your main() code in a + * TRY_* block, your entire application is protected. + * If any subfunction fails, execution is transferred to + * the closest CATCH() that was installed (as with C++ + * try and catch). + * + * <B>Asynchronous exceptions</B> + * + * The exception handlers in this file (which are installed + * with the TRY/CATCH mechanism) only intercept synchronous + * exceptions, most importantly, XCPT_ACCESS_VIOLATION (see + * excHandlerLoud for a list). They do not protect your code + * against asynchronous exceptions. + * + * OS/2 defines asynchronous exceptions to be those that + * can be delayed. With OS/2, there are only three of these: + * + * -- XCPT_PROCESS_TERMINATE + * -- XCPT_ASYNC_PROCESS_TERMINATE + * -- XCPT_SIGNAL (thread 1 only) + * + * To protect yourself against these also, put the section + * in question in a DosEnterMustComplete/DosExitMustComplete + * block as well. + * + * <B>Mutex semaphores</B> + * + * The problem with OS/2 mutex semaphores is that they are + * sometimes not automatically released when a thread terminates. + * If there are several mutexes involved and they are released + * in improper order, you can get zombie threads on exit. + * Even worse, if this happens to a PM thread, this will hang + * the system. + * + * As a result, you should protect any section of code which + * requests a semaphore with the exception handlers. + * + * So _whenever_ you request a mutex semaphore, enclose + * the block with TRY/CATCH in case the code crashes. + * Besides, enclose the TRY/CATCH block in a must-complete + * section, like this: + * + + HMTX hmtx = ... + + + + int your_func(int) + + { + + BOOL fSemOwned = FALSE; + + + + TRY_QUIET(excpt1) // or TRY_LOUD + + { + + if (fSemOwned = !DosRequestMutexSem(hmtx, ...)) + + { ... // work on your protected data + + } + + // mutex gets released below + + } + + CATCH(excpt1) { } END_CATCH(); // always needed! + + + + if (fSemOwned) + + // this gets executed always, even if an exception occured + + DosReleaseMutexSem(hmtx); + + } // end of your_func + * + * This way your mutex semaphore gets released in every + * possible condition. + * + * <B>Customizing</B> + * + * As opposed to versions before 0.9.0, this code is now + * completely independent of XWorkplace. This file now + * contains "pure" exception handlers only. + * + * However, you can customize these exception handlers by + * calling excRegisterHooks. This is what XWorkplace does now. + * This should be done upon initialization of your application. + * If excRegisterHooks is not called, the following safe + * defaults are used: + * + * -- the trap log file is TRAP.LOG in the root + * directory of your boot drive. + * + * For details on the provided exception handlers, refer + * to excHandlerLoud and excHandlerQuiet. + * + * More useful debug information can be found in the "OS/2 Debugging + * Handbook", which is now available in INF format on the IBM + * DevCon site ("http://service2.boulder.ibm.com/devcon/"). + * This book shows worked examples of how to unwind a stack dump. + * + * This file incorporates code from the following: + * -- Monte Copeland, IBM Boca Ration, Florida, USA (1993) + * -- Roman Stangl, from the Program Commander/2 sources + * (1997-98) + * -- Marc Fiammante, John Currier, Kim Rasmussen, + * Anthony Cruise (EXCEPT3.ZIP package for a generic + * exception handling DLL, available at Hobbes). + * + * If not explicitly stated otherwise, the code has been written + * by me, Ulrich M�ller. + * + * Note: Version numbering in this file relates to XWorkplace version + * numbering. + * + *@@header "helpers\except.h" + */ + +/* + * This file Copyright (C) 1992-99 Ulrich M�ller, + * Monte Copeland, + * Roman Stangl, + * Kim Rasmussen, + * Marc Fiammante, + * John Currier, + * Anthony Cruise. + * This file is part of the "XWorkplace helpers" source package. + * + * 2009-06-15 published under LGPL3 with Ulrich M�ller permission. + * + */ + +#define OS2EMX_PLAIN_CHAR + // this is needed for "os2emx.h"; if this is defined, + // emx will define PSZ as _signed_ char, otherwise + // as unsigned char + +#define INCL_DOSMODULEMGR +#define INCL_DOSEXCEPTIONS +#define INCL_DOSPROCESS +#define INCL_DOSMISC +#define INCL_DOSERRORS +#include <os2.h> + +// C library headers +#include <stdio.h> // needed for except.h +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <setjmp.h> // needed for except.h +#include <assert.h> // needed for except.h + +#define DONT_REPLACE_MALLOC +#include "helpers\setup.h" // code generation and debugging options + +// headers in /helpers +#include "helpers\dosh.h" // Control Program helper routines +#include "helpers\except.h" // exception handling +#include "helpers\debug.h" // symbol/debug code analysis + +#pragma hdrstop + +/* ****************************************************************** + * + * Global variables + * + ********************************************************************/ + +// hooks to be registered using excRegisterHooks +PFNEXCOPENFILE G_pfnExcOpenFile = 0; +PFNEXCHOOK G_pfnExcHook = 0; +PFNEXCHOOKERROR G_pfnExcHookError = 0; +// beep flag for excHandlerLoud +BOOL G_fBeepOnException = TRUE; + +ULONG G_ulExplainExceptionRunning = 0; + // global flag which is != 0 if some exception handler + // is inside excExplainException, so that XShutdown can + // wait until the trap log is done; + // this is exported thru except.h + // V0.9.13 (2001-06-19) [umoeller] + +/* + *@@category: Helpers\Control program helpers\Exceptions/debugging + * See except.c. + */ + +/* ****************************************************************** + * + * Exception helper routines + * + ********************************************************************/ + +/* + *@@ excDescribePage: + * + */ + +VOID excDescribePage(FILE *file, ULONG ulCheck) +{ + APIRET arc; + ULONG ulCountPages = 1; + ULONG ulFlagsPage = 0; + arc = DosQueryMem((PVOID)ulCheck, &ulCountPages, &ulFlagsPage); + + if (arc == NO_ERROR) + { + fprintf(file, "valid, flags: "); + if (ulFlagsPage & PAG_READ) + fprintf(file, "read "); + if (ulFlagsPage & PAG_WRITE) + fprintf(file, "write "); + if (ulFlagsPage & PAG_EXECUTE) + fprintf(file, "execute "); + if (ulFlagsPage & PAG_GUARD) + fprintf(file, "guard "); + if (ulFlagsPage & PAG_COMMIT) + fprintf(file, "committed "); + if (ulFlagsPage & PAG_SHARED) + fprintf(file, "shared "); + if (ulFlagsPage & PAG_FREE) + fprintf(file, "free "); + if (ulFlagsPage & PAG_BASE) + fprintf(file, "base "); + } + else if (arc == ERROR_INVALID_ADDRESS) + fprintf(file, "invalid"); +} + +/* + *@@ excPrintStackFrame: + * wrapper for dbgPrintStackFrame to format + * output stuff right. + * + *@@added V0.9.2 (2000-03-10) [umoeller] + *@@changed V0.9.12 (2001-05-12) [umoeller]: added seg:ofs to output always + */ + +VOID excPrintStackFrame(FILE *file, // in: output log file + PSZ pszDescription, // in: description for stack frame (should be eight chars) + ULONG ulAddress) // in: address to debug +{ + APIRET arc = NO_ERROR; + HMODULE hmod1 = NULLHANDLE; + CHAR szMod1[2*CCHMAXPATH] = "unknown"; + ULONG ulObject = 0, + ulOffset = 0; + fprintf(file, + " %-8s: %08lX ", + pszDescription, + ulAddress); + arc = DosQueryModFromEIP(&hmod1, + &ulObject, + sizeof(szMod1), szMod1, + &ulOffset, + ulAddress); + + if (arc != NO_ERROR) + { + // error: + fprintf(file, + " %-8s Error: DosQueryModFromEIP returned %lu\n", + szMod1, + arc); + } + else + { + CHAR szFullName[2*CCHMAXPATH]; + + fprintf(file, + " %-8s %02lX:%08lX\n ", + szMod1, + ulObject + 1, // V0.9.12 (2001-05-12) [umoeller] + ulOffset); // V0.9.12 (2001-05-12) [umoeller] + + DosQueryModuleName(hmod1, sizeof(szFullName), szFullName); + dbgPrintStackFrame(file, + szFullName, + ulObject, + ulOffset); + + fprintf(file, "\n"); + + // make a 'tick' sound to let the user know we're still alive + DosBeep(2000, 10); + } +} + +/* + *@@ excDumpStackFrames: + * called from excExplainException to dump the + * thread's stack frames. This calls excPrintStackFrame + * for each stack frame found. + * + *@@added V0.9.4 (2000-06-15) [umoeller] + */ + +VOID excDumpStackFrames(FILE *file, // in: logfile from fopen() + PTIB ptib, + PCONTEXTRECORD pContextRec) // in: excpt info +{ + PULONG pulStackWord = 0; + + fprintf(file, "\n\nStack frames:\n Address Module seg:ofs\n"); + + // first the trapping address itself + excPrintStackFrame(file, + "CS:EIP ", + pContextRec->ctx_RegEip); + + + pulStackWord = (PULONG)pContextRec->ctx_RegEbp; + /* if (pContextRec->ctx_RegEbp < pContextRec->ctx_RegEsp) + pulStackWord = (PULONG)(pContextRec->ctx_RegEbp & 0xFFFFFFF0); + else + pulStackWord = (PULONG)(pContextRec->ctx_RegEsp & 0xFFFFFFF0); */ + + while ( (pulStackWord != 0) + && (pulStackWord < (PULONG)ptib->tib_pstacklimit) + ) + { + CHAR szAddress[20]; + + if (((ULONG)pulStackWord & 0x00000FFF) == 0x00000000) + { + // we're on a page boundary: check access + ULONG ulCountPages = 0x1000; + ULONG ulFlagsPage = 0; + APIRET arc = DosQueryMem((void *)pulStackWord, + &ulCountPages, + &ulFlagsPage); + if ( (arc != NO_ERROR) + || ( (arc == NO_ERROR) + && ( !( ((ulFlagsPage & (PAG_COMMIT|PAG_READ)) + == (PAG_COMMIT|PAG_READ) + ) + ) + ) + ) + ) + { + fprintf(file, "\n %08lX: ", (ULONG)pulStackWord); + fprintf(file, "Page inaccessible"); + pulStackWord += 0x1000; + continue; // for + } + } + + sprintf(szAddress, "%08lX", + (ULONG)pulStackWord); + excPrintStackFrame(file, + szAddress, + *(pulStackWord+1)); + pulStackWord = (PULONG)*(pulStackWord); + + if (pulStackWord == 0) + fprintf(file, "\n pulStackWord == 0"); + else if (pulStackWord >= (PULONG)ptib->tib_pstacklimit) + fprintf(file, "\n pulStackWord >= (PULONG)ptib->tib_pstacklimit"); + } // end while +} + +/* + *@@ excExplainException: + * used by the exception handlers below to write + * LOTS of information about the exception into a logfile. + * + * This calls excPrintStackFrame for each stack frame. + * + *@@changed V0.9.0 [umoeller]: added support for application hook + *@@changed V0.9.0 (99-11-02) [umoeller]: added TID to dump + *@@changed V0.9.2 (2000-03-10) [umoeller]: now using excPrintStackFrame + *@@changed V0.9.3 (2000-05-03) [umoeller]: fixed crashes + *@@changed V0.9.6 (2000-11-06) [umoeller]: added more register dumps + *@@changed V0.9.13 (2001-06-19) [umoeller]: added global flag for whether this is running + *@@changed V0.9.16 (2001-11-02) [pr]: make object display signed + *@@changed V0.9.19 (2002-03-28) [umoeller]: added thread ordinal + */ + +VOID excExplainException(FILE *file, // in: logfile from fopen() + PSZ pszHandlerName, // in: descriptive string + PEXCEPTIONREPORTRECORD pReportRec, // in: excpt info + PCONTEXTRECORD pContextRec) // in: excpt info +{ + ULONG aulBuf[3]; + const char *pcszVersion = "unknown"; + + PTIB ptib = NULL; + PPIB ppib = NULL; + HMODULE hMod1, hMod2; + CHAR szMod1[CCHMAXPATH] = "unknown", + szMod2[CCHMAXPATH] = "unknown"; + ULONG ulObjNum, + ulOffset; + ULONG ul; + + ULONG ulOldPriority = 0x0100; // regular, delta 0 + + // raise global flag for whether this func is running + // V0.9.13 (2001-06-19) [umoeller] + G_ulExplainExceptionRunning++; + + // raise this thread's priority, because this + // might take some time + if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR) + if (ptib) + if (ptib->tib_ptib2) + { + ulOldPriority = ptib->tib_ptib2->tib2_ulpri; + DosSetPriority(PRTYS_THREAD, + PRTYC_REGULAR, + PRTYD_MAXIMUM, + 0); // current thread + } + + // make some noise +#ifndef __NOEXCEPTIONBEEPS__ // V0.9.19 (2002-04-17) [umoeller] + if (G_fBeepOnException) + { + DosBeep( 250, 30); + DosBeep( 500, 30); + DosBeep(1000, 30); + DosBeep(2000, 30); + DosBeep(4000, 30); + DosBeep(2000, 30); + DosBeep(1000, 30); + DosBeep( 500, 30); + DosBeep( 250, 30); + } +#endif + + // generic exception info + DosQuerySysInfo(QSV_VERSION_MAJOR, // 11 + QSV_VERSION_MINOR, // 12 + &aulBuf, sizeof(aulBuf)); + // Warp 3 is reported as 20.30 + // Warp 4 is reported as 20.40 + // Aurora is reported as 20.45 + + if (aulBuf[0] == 20) + { + switch (aulBuf[1]) + { + case 30: pcszVersion = "Warp 3"; break; + case 40: pcszVersion = "Warp 4"; break; + case 45: pcszVersion = "WSeB kernel"; break; + } + } + fprintf(file, + "Running OS/2 version: %u.%u (%s)\n", + aulBuf[0], // major + aulBuf[1], + pcszVersion); + + + // generic exception info + fprintf(file, + "\n%s:\n Exception type: %08lX\n Address: %08lX\n Params: ", + pszHandlerName, + pReportRec->ExceptionNum, + (ULONG)pReportRec->ExceptionAddress); + for (ul = 0; ul < pReportRec->cParameters; ul++) + { + fprintf(file, "%08lX ", + pReportRec->ExceptionInfo[ul]); + } + + // now explain the exception in a bit more detail; + // depending on the exception, pReportRec->ExceptionInfo + // contains some useful data + switch (pReportRec->ExceptionNum) + { + case XCPT_ACCESS_VIOLATION: + fprintf(file, "\nXCPT_ACCESS_VIOLATION: "); + if (pReportRec->ExceptionInfo[0] & XCPT_READ_ACCESS) + fprintf(file, "Invalid read access from 0x%04lX:%08lX.\n", + pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]); + else if (pReportRec->ExceptionInfo[0] & XCPT_WRITE_ACCESS) + fprintf(file, "Invalid write access to 0x%04lX:%08lX.\n", + pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]); + else if (pReportRec->ExceptionInfo[0] & XCPT_SPACE_ACCESS) + fprintf(file, "Invalid space access at 0x%04lX.\n", + pReportRec->ExceptionInfo[1]); + else if (pReportRec->ExceptionInfo[0] & XCPT_LIMIT_ACCESS) + fprintf(file, "Invalid limit access occurred.\n"); + else if (pReportRec->ExceptionInfo[0] == XCPT_UNKNOWN_ACCESS) + fprintf(file, "unknown at 0x%04lX:%08lX\n", + pContextRec->ctx_SegDs, pReportRec->ExceptionInfo[1]); + fprintf(file, + "Explanation: An attempt was made to access a memory object which does\n" + " not belong to the current process. Most probable causes\n" + " for this are that an invalid pointer was used, there was\n" + " confusion with administering memory or error conditions \n" + " were not properly checked for.\n"); + break; + + case XCPT_INTEGER_DIVIDE_BY_ZERO: + fprintf(file, "\nXCPT_INTEGER_DIVIDE_BY_ZERO.\n"); + fprintf(file, + "Explanation: An attempt was made to divide an integer value by zero,\n" + " which is not defined.\n"); + break; + + case XCPT_ILLEGAL_INSTRUCTION: + fprintf(file, "\nXCPT_ILLEGAL_INSTRUCTION.\n"); + fprintf(file, + "Explanation: An attempt was made to execute an instruction that\n" + " is not defined on this machine's architecture.\n"); + break; + + case XCPT_PRIVILEGED_INSTRUCTION: + fprintf(file, "\nXCPT_PRIVILEGED_INSTRUCTION.\n"); + fprintf(file, + "Explanation: An attempt was made to execute an instruction that\n" + " is not permitted in the current machine mode or that\n" + " the program had no permission to execute.\n"); + break; + + case XCPT_INTEGER_OVERFLOW: + fprintf(file, "\nXCPT_INTEGER_OVERFLOW.\n"); + fprintf(file, + "Explanation: An integer operation generated a carry-out of the most\n" + " significant bit. This is a sign of an attempt to store\n" + " a value which does not fit into an integer variable.\n"); + break; + + default: + fprintf(file, "\nUnknown OS/2 exception number %d.\n", pReportRec->ExceptionNum); + fprintf(file, "Look this up in the OS/2 header files.\n"); + break; + } + + // V0.9.16 (2001-11-02) [pr]: We already got this info. above - this overwrites the + // original values before the priority change, which is rather confusing. + // if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR) + { + /* + * process info: + * + */ + + if ((ptib) && (ppib)) // (99-11-01) [umoeller] + { + if (pContextRec->ContextFlags & CONTEXT_CONTROL) + { + // get the main module + hMod1 = ppib->pib_hmte; + DosQueryModuleName(hMod1, + sizeof(szMod1), + szMod1); + + // get the trapping module + DosQueryModFromEIP(&hMod2, + &ulObjNum, + sizeof(szMod2), + szMod2, + &ulOffset, + pContextRec->ctx_RegEip); + DosQueryModuleName(hMod2, + sizeof(szMod2), + szMod2); + } + + fprintf(file, + "\nProcess information:" + "\n Process ID: 0x%lX" + "\n Process module: 0x%lX (%s)" + "\n Trapping module: 0x%lX (%s)" + "\n Object: %ld\n", // V0.9.16 (2001-11-02) [pr]: make this display signed + ppib->pib_ulpid, + hMod1, szMod1, + hMod2, szMod2, + ulObjNum); + + fprintf(file, + "\nTrapping thread information:" + "\n Thread ID: 0x%lX (%lu)" + "\n Thread slot ID: 0x%lX (%lu)" // added V0.9.19 (2002-03-28) [umoeller] + "\n Priority: 0x%lX\n", + ptib->tib_ptib2->tib2_ultid, ptib->tib_ptib2->tib2_ultid, + ptib->tib_ordinal, ptib->tib_ordinal, + ulOldPriority); + } + else + fprintf(file, "\nProcess information was not available."); + + /* + * now call the hook, if one has been defined, + * so that the application can write additional + * information to the traplog (V0.9.0) + */ + + if (G_pfnExcHook) + G_pfnExcHook(file, ptib, ulOldPriority); // V0.9.16 (2001-12-02) [pr] + + // *** registers + + fprintf(file, "\nRegisters:"); + if (pContextRec->ContextFlags & CONTEXT_INTEGER) + { + // DS the following 4 added V0.9.6 (2000-11-06) [umoeller] + fprintf(file, "\n DS = %08lX ", pContextRec->ctx_SegDs); + excDescribePage(file, pContextRec->ctx_SegDs); + // ES + fprintf(file, "\n ES = %08lX ", pContextRec->ctx_SegEs); + excDescribePage(file, pContextRec->ctx_SegEs); + // FS + fprintf(file, "\n FS = %08lX ", pContextRec->ctx_SegFs); + excDescribePage(file, pContextRec->ctx_SegFs); + // GS + fprintf(file, "\n GS = %08lX ", pContextRec->ctx_SegGs); + excDescribePage(file, pContextRec->ctx_SegGs); + + // EAX + fprintf(file, "\n EAX = %08lX ", pContextRec->ctx_RegEax); + excDescribePage(file, pContextRec->ctx_RegEax); + // EBX + fprintf(file, "\n EBX = %08lX ", pContextRec->ctx_RegEbx); + excDescribePage(file, pContextRec->ctx_RegEbx); + // ECX + fprintf(file, "\n ECX = %08lX ", pContextRec->ctx_RegEcx); + excDescribePage(file, pContextRec->ctx_RegEcx); + // EDX + fprintf(file, "\n EDX = %08lX ", pContextRec->ctx_RegEdx); + excDescribePage(file, pContextRec->ctx_RegEdx); + // ESI + fprintf(file, "\n ESI = %08lX ", pContextRec->ctx_RegEsi); + excDescribePage(file, pContextRec->ctx_RegEsi); + // EDI + fprintf(file, "\n EDI = %08lX ", pContextRec->ctx_RegEdi); + excDescribePage(file, pContextRec->ctx_RegEdi); + fprintf(file, "\n"); + } + else + fprintf(file, " not available\n"); + + if (pContextRec->ContextFlags & CONTEXT_CONTROL) + { + + // *** instruction + + fprintf(file, "Instruction pointer (where exception occured):\n CS:EIP = %04lX:%08lX ", + pContextRec->ctx_SegCs, + pContextRec->ctx_RegEip); + excDescribePage(file, pContextRec->ctx_RegEip); + + // *** CPU flags + + fprintf(file, "\n EFLAGS = %08lX", pContextRec->ctx_EFlags); + + /* + * stack: + * + */ + + fprintf(file, "\nStack:\n Base: %08lX\n Limit: %08lX", + (ULONG)(ptib ? ptib->tib_pstack : 0), + (ULONG)(ptib ? ptib->tib_pstacklimit : 0)); + fprintf(file, "\n SS:ESP = %04lX:%08lX ", + pContextRec->ctx_SegSs, + pContextRec->ctx_RegEsp); + excDescribePage(file, pContextRec->ctx_RegEsp); + + fprintf(file, "\n EBP = %08lX ", pContextRec->ctx_RegEbp); + excDescribePage(file, pContextRec->ctx_RegEbp); + + /* + * stack dump: + */ + + if (ptib != 0) + { + excDumpStackFrames(file, ptib, pContextRec); + } + } + } + fprintf(file, "\n"); + + // reset old priority + DosSetPriority(PRTYS_THREAD, + (ulOldPriority & 0x0F00) >> 8, + (UCHAR)ulOldPriority, + 0); // current thread + + // lower global flag again V0.9.13 (2001-06-19) [umoeller] + G_ulExplainExceptionRunning--; +} + +/* ****************************************************************** + * + * Exported routines + * + ********************************************************************/ + +/* + *@@ excRegisterHooks: + * this registers hooks which get called for + * exception handlers. You can set any of the + * hooks to NULL for safe defaults (see top of + * except.c for details). You can set none, + * one, or both of the hooks, and you can call + * this function several times. + * + * Both hooks get called whenever an exception + * occurs, so there better be no bugs in these + * routines. ;-) They only get called from + * within excHandlerLoud (because excHandlerQuiet + * writes no trap logs). + * + * The hooks are as follows: + * + * -- pfnExcOpenFileNew gets called to open + * the trap log file. This must return a FILE* + * pointer from fopen(). If this is not defined, + * ?:\TRAP.LOG is used. Use this to specify a + * different file and have some notes written + * into it before the actual exception info. + * + * -- pfnExcHookNew gets called while the trap log + * is being written. At this point, + * the following info has been written into + * the trap log already: + * -- exception type/address block + * -- exception explanation + * -- process information + * + * _After_ the hook, the exception handler + * continues with the "Registers" information + * and stack dump/analysis. + * + * Use this hook to write additional application + * info into the trap log, such as the state + * of your own threads and mutexes. + * + * -- pfnExcHookError gets called when the TRY_* macros + * fail to install an exception handler (when + * DosSetExceptionHandler fails). I've never seen + * this happen. + * + *@@added V0.9.0 [umoeller] + *@@changed V0.9.2 (2000-03-10) [umoeller]: pfnExcHookError added + */ + +VOID excRegisterHooks(PFNEXCOPENFILE pfnExcOpenFileNew, + PFNEXCHOOK pfnExcHookNew, + PFNEXCHOOKERROR pfnExcHookError, + BOOL fBeepOnExceptionNew) +{ + // adjust the global variables + G_pfnExcOpenFile = pfnExcOpenFileNew; + G_pfnExcHook = pfnExcHookNew; + G_pfnExcHookError = pfnExcHookError; + G_fBeepOnException = fBeepOnExceptionNew; +} + +/* + *@@ excHandlerLoud: + * this is the "sophisticated" exception handler; + * which gives forth a loud sequence of beeps thru the + * speaker, writes a trap log and then returns back + * to the thread to continue execution, i.e. the + * default OS/2 exception handler will never get + * called. + * + * This requires a setjmp() call on + * EXCEPTIONREGISTRATIONRECORD2.jmpThread before + * being installed. The TRY_LOUD macro will take + * care of this for you (see except.c). + * + * This intercepts the following exceptions (see + * the OS/2 Control Program Reference for details): + * + * -- XCPT_ACCESS_VIOLATION (traps 0x0d, 0x0e) + * -- XCPT_INTEGER_DIVIDE_BY_ZERO (trap 0) + * -- XCPT_ILLEGAL_INSTRUCTION (trap 6) + * -- XCPT_PRIVILEGED_INSTRUCTION + * -- XCPT_INTEGER_OVERFLOW (trap 4) + * + * For these exceptions, we call the functions in debug.c + * to try to find debug code or SYM file information about + * what source code corresponds to the error. + * + * See excRegisterHooks for the default setup of this. + * + * Note that to get meaningful debugging information + * in this handler's traplog, you need the following: + * + * a) have a MAP file created at link time (/MAP) + * + * b) convert the MAP to a SYM file using MAPSYM + * + * c) put the SYM file in the same directory of + * the module (EXE or DLL). This must have the + * same filestem as the module. + * + * All other exceptions are passed to the next handler + * in the exception handler chain. This might be the + * C/C++ compiler handler or the default OS/2 handler, + * which will probably terminate the process. + * + *@@changed V0.9.0 [umoeller]: added support for thread termination + *@@changed V0.9.2 (2000-03-10) [umoeller]: switched date format to ISO + *@@changed V0.9.19 (2002-05-07) [umoeller]: added EXCEPTIONREPORTRECORD info so that catch block can check that + */ + +ULONG _System excHandlerLoud(PEXCEPTIONREPORTRECORD pReportRec, + PEXCEPTIONREGISTRATIONRECORD2 pRegRec2, + PCONTEXTRECORD pContextRec, + PVOID pv) +{ + /* From the VAC++3 docs: + * "The first thing an exception handler should do is check the + * exception flags. If EH_EXIT_UNWIND is set, meaning + * the thread is ending, the handler tells the operating system + * to pass the exception to the next exception handler. It does the + * same if the EH_UNWINDING flag is set, the flag that indicates + * this exception handler is being removed. + * The EH_NESTED_CALL flag indicates whether the exception + * occurred within an exception handler. If the handler does + * not check this flag, recursive exceptions could occur until + * there is no stack remaining." + * So for all these conditions, we exit immediately. + */ + + if (pReportRec->fHandlerFlags & EH_EXIT_UNWIND) + return (XCPT_CONTINUE_SEARCH); + if (pReportRec->fHandlerFlags & EH_UNWINDING) + return (XCPT_CONTINUE_SEARCH); + if (pReportRec->fHandlerFlags & EH_NESTED_CALL) + return (XCPT_CONTINUE_SEARCH); + + switch (pReportRec->ExceptionNum) + { + case XCPT_ACCESS_VIOLATION: + case XCPT_INTEGER_DIVIDE_BY_ZERO: + case XCPT_ILLEGAL_INSTRUCTION: + case XCPT_PRIVILEGED_INSTRUCTION: + case XCPT_INVALID_LOCK_SEQUENCE: + case XCPT_INTEGER_OVERFLOW: + { + // "real" exceptions: + FILE *file; + + // open traplog file; + if (G_pfnExcOpenFile) + // hook defined for this: call it + file = (*G_pfnExcOpenFile)(); + else + { + CHAR szFileName[100]; + // no hook defined: open some + // default traplog file in root directory of + // boot drive + sprintf(szFileName, "%c:\\trap.log", doshQueryBootDrive()); + file = fopen(szFileName, "a"); + + if (file) + { + DATETIME DT; + DosGetDateTime(&DT); + fprintf(file, + "\nTrap message -- Date: %04d-%02d-%02d, Time: %02d:%02d:%02d\n", + DT.year, DT.month, DT.day, + DT.hours, DT.minutes, DT.seconds); + fprintf(file, "------------------------------------------------\n"); + + } + } + + // write error log + excExplainException(file, + "excHandlerLoud", + pReportRec, + pContextRec); + fclose(file); + + // copy report rec to user buffer + // V0.9.19 (2002-05-07) [umoeller] + memcpy(&pRegRec2->err, + pReportRec, + sizeof(EXCEPTIONREPORTRECORD)); + + // jump back to failing routine + longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum); + break; } + } + + // not handled + return (XCPT_CONTINUE_SEARCH); +} + +/* + *@@ excHandlerQuiet: + * "quiet" xcpt handler, which simply suppresses exceptions; + * this is useful for certain error-prone functions, where + * exceptions are likely to appear, for example used by + * wpshCheckObject to implement a fail-safe SOM object check. + * + * This does _not_ write an error log and makes _no_ sound. + * This simply jumps back to the trapping thread or + * calls EXCEPTIONREGISTRATIONRECORD2.pfnOnKill. + * + * Other than that, this behaves like excHandlerLoud. + * + * This is best registered thru the TRY_QUIET macro + * (new with V0.84, described in except.c), which + * does the necessary setup. + * + *@@changed V0.9.0 [umoeller]: added support for thread termination + *@@changed V0.9.19 (2002-05-07) [umoeller]: added EXCEPTIONREPORTRECORD info so that catch block can check that + */ + +ULONG _System excHandlerQuiet(PEXCEPTIONREPORTRECORD pReportRec, + PEXCEPTIONREGISTRATIONRECORD2 pRegRec2, + PCONTEXTRECORD pContextRec, + PVOID pv) +{ + if (pReportRec->fHandlerFlags & EH_EXIT_UNWIND) + return (XCPT_CONTINUE_SEARCH); + if (pReportRec->fHandlerFlags & EH_UNWINDING) + return (XCPT_CONTINUE_SEARCH); + if (pReportRec->fHandlerFlags & EH_NESTED_CALL) + return (XCPT_CONTINUE_SEARCH); + + switch (pReportRec->ExceptionNum) + { + case XCPT_ACCESS_VIOLATION: + case XCPT_INTEGER_DIVIDE_BY_ZERO: + case XCPT_ILLEGAL_INSTRUCTION: + case XCPT_PRIVILEGED_INSTRUCTION: + case XCPT_INVALID_LOCK_SEQUENCE: + case XCPT_INTEGER_OVERFLOW: + // write excpt explanation only if the + // resp. debugging #define is set (setup.h) + #ifdef DEBUG_WRITEQUIETEXCPT + { + FILE *file = excOpenTraplogFile(); + excExplainException(file, + "excHandlerQuiet", + pReportRec, + pContextRec); + fclose(file); + } + #endif + + // copy report rec to user buffer + // V0.9.19 (2002-05-07) [umoeller] + memcpy(&pRegRec2->err, + pReportRec, + sizeof(EXCEPTIONREPORTRECORD)); + + // jump back to failing routine + longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum); + break; + + default: + break; + } + + return (XCPT_CONTINUE_SEARCH); +} + + |