home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwphescr.zip / XWPH0208.ZIP / src / helpers / except.c < prev    next >
C/C++ Source or Header  |  2002-07-24  |  40KB  |  1,039 lines

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