| 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 occurred: 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 | +              volatile 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 occurred | 
|---|
| 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 Mller. | 
|---|
| 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-2005 Ulrich Mller, | 
|---|
| 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 | *@@changed V1.0.0 (2002-08-28) [umoeller]: added OS revision to dump | 
|---|
| 454 | */ | 
|---|
| 455 |  | 
|---|
| 456 | VOID excExplainException(FILE *file,                   // in: logfile from fopen() | 
|---|
| 457 | PSZ pszHandlerName,           // in: descriptive string | 
|---|
| 458 | PEXCEPTIONREPORTRECORD pReportRec, // in: excpt info | 
|---|
| 459 | PCONTEXTRECORD pContextRec)   // in: excpt info | 
|---|
| 460 | { | 
|---|
| 461 | ULONG       aulBuf[3]; | 
|---|
| 462 | const char  *pcszVersion = "unknown"; | 
|---|
| 463 |  | 
|---|
| 464 | PTIB        ptib = NULL; | 
|---|
| 465 | PPIB        ppib = NULL; | 
|---|
| 466 | HMODULE     hMod1, hMod2; | 
|---|
| 467 | CHAR        szMod1[CCHMAXPATH] = "unknown", | 
|---|
| 468 | szMod2[CCHMAXPATH] = "unknown"; | 
|---|
| 469 | ULONG       ulObjNum, | 
|---|
| 470 | ulOffset; | 
|---|
| 471 | ULONG       ul; | 
|---|
| 472 |  | 
|---|
| 473 | ULONG       ulOldPriority = 0x0100; // regular, delta 0 | 
|---|
| 474 |  | 
|---|
| 475 | // raise global flag for whether this func is running | 
|---|
| 476 | // V0.9.13 (2001-06-19) [umoeller] | 
|---|
| 477 | G_ulExplainExceptionRunning++; | 
|---|
| 478 |  | 
|---|
| 479 | // raise this thread's priority, because this | 
|---|
| 480 | // might take some time | 
|---|
| 481 | if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR) | 
|---|
| 482 | if (ptib) | 
|---|
| 483 | if (ptib->tib_ptib2) | 
|---|
| 484 | { | 
|---|
| 485 | ulOldPriority = ptib->tib_ptib2->tib2_ulpri; | 
|---|
| 486 | DosSetPriority(PRTYS_THREAD, | 
|---|
| 487 | PRTYC_REGULAR, | 
|---|
| 488 | PRTYD_MAXIMUM, | 
|---|
| 489 | 0);     // current thread | 
|---|
| 490 | } | 
|---|
| 491 |  | 
|---|
| 492 | // make some noise | 
|---|
| 493 | #ifndef __NOEXCEPTIONBEEPS__        // V0.9.19 (2002-04-17) [umoeller] | 
|---|
| 494 | if (G_fBeepOnException) | 
|---|
| 495 | { | 
|---|
| 496 | DosBeep( 250, 30); | 
|---|
| 497 | DosBeep( 500, 30); | 
|---|
| 498 | DosBeep(1000, 30); | 
|---|
| 499 | DosBeep(2000, 30); | 
|---|
| 500 | DosBeep(4000, 30); | 
|---|
| 501 | DosBeep(2000, 30); | 
|---|
| 502 | DosBeep(1000, 30); | 
|---|
| 503 | DosBeep( 500, 30); | 
|---|
| 504 | DosBeep( 250, 30); | 
|---|
| 505 | } | 
|---|
| 506 | #endif | 
|---|
| 507 |  | 
|---|
| 508 | // generic exception info | 
|---|
| 509 | DosQuerySysInfo(QSV_VERSION_MAJOR,      // 11 | 
|---|
| 510 | QSV_VERSION_REVISION,   // 13 V1.0.0 (2002-08-28) [umoeller] | 
|---|
| 511 | &aulBuf, sizeof(aulBuf)); | 
|---|
| 512 | // Warp 3 is reported as 20.30 | 
|---|
| 513 | // Warp 4 is reported as 20.40 | 
|---|
| 514 | // Aurora is reported as 20.45 | 
|---|
| 515 |  | 
|---|
| 516 | if (aulBuf[0] == 20) | 
|---|
| 517 | { | 
|---|
| 518 | switch (aulBuf[1]) | 
|---|
| 519 | { | 
|---|
| 520 | case 30: pcszVersion = "Warp 3"; break; | 
|---|
| 521 | case 40: pcszVersion = "Warp 4"; break; | 
|---|
| 522 | case 45: pcszVersion = "WSeB kernel"; break; | 
|---|
| 523 | } | 
|---|
| 524 | } | 
|---|
| 525 | fprintf(file, | 
|---|
| 526 | "Running OS/2 version: %u.%u.%u (%s)\n", | 
|---|
| 527 | aulBuf[0],                      // major | 
|---|
| 528 | aulBuf[1], | 
|---|
| 529 | aulBuf[2],              // revision V1.0.0 (2002-08-28) [umoeller] | 
|---|
| 530 | pcszVersion); | 
|---|
| 531 |  | 
|---|
| 532 |  | 
|---|
| 533 | // generic exception info | 
|---|
| 534 | fprintf(file, | 
|---|
| 535 | "\n%s:\n    Exception type: %08lX\n    Address:        %08lX\n    Params:         ", | 
|---|
| 536 | pszHandlerName, | 
|---|
| 537 | pReportRec->ExceptionNum, | 
|---|
| 538 | (ULONG)pReportRec->ExceptionAddress); | 
|---|
| 539 | for (ul = 0;  ul < pReportRec->cParameters;  ul++) | 
|---|
| 540 | { | 
|---|
| 541 | fprintf(file, "%08lX  ", | 
|---|
| 542 | pReportRec->ExceptionInfo[ul]); | 
|---|
| 543 | } | 
|---|
| 544 |  | 
|---|
| 545 | // now explain the exception in a bit more detail; | 
|---|
| 546 | // depending on the exception, pReportRec->ExceptionInfo | 
|---|
| 547 | // contains some useful data | 
|---|
| 548 | switch (pReportRec->ExceptionNum) | 
|---|
| 549 | { | 
|---|
| 550 | case XCPT_ACCESS_VIOLATION: | 
|---|
| 551 | fprintf(file, "\nXCPT_ACCESS_VIOLATION: "); | 
|---|
| 552 | if (pReportRec->ExceptionInfo[0] & XCPT_READ_ACCESS) | 
|---|
| 553 | fprintf(file, "XCPT_READ_ACCESS -- Invalid read access from 0x%04lX:%08lX.\n", | 
|---|
| 554 | pContextRec->ctx_SegDs, | 
|---|
| 555 | pReportRec->ExceptionInfo[1]); | 
|---|
| 556 | else if (pReportRec->ExceptionInfo[0] & XCPT_WRITE_ACCESS) | 
|---|
| 557 | fprintf(file, "XCPT_WRITE_ACCESS -- Invalid write access to 0x%04lX:%08lX.\n", | 
|---|
| 558 | pContextRec->ctx_SegDs, | 
|---|
| 559 | pReportRec->ExceptionInfo[1]); | 
|---|
| 560 | else if (pReportRec->ExceptionInfo[0] & XCPT_SPACE_ACCESS) | 
|---|
| 561 | fprintf(file, "XCPT_SPACE_ACCESS -- Invalid space access at 0x%04lX.\n", | 
|---|
| 562 | pReportRec->ExceptionInfo[1]); | 
|---|
| 563 | else if (pReportRec->ExceptionInfo[0] & XCPT_LIMIT_ACCESS) | 
|---|
| 564 | fprintf(file, "XCPT_LIMIT_ACCESS -- Invalid limit access occurred.\n"); | 
|---|
| 565 | else if (pReportRec->ExceptionInfo[0] == XCPT_UNKNOWN_ACCESS) | 
|---|
| 566 | fprintf(file, "XCPT_UNKNOWN_ACCESS -- unknown at 0x%04lX:%08lX\n", | 
|---|
| 567 | pContextRec->ctx_SegDs, | 
|---|
| 568 | pReportRec->ExceptionInfo[1]); | 
|---|
| 569 | fprintf(file, | 
|---|
| 570 | "Explanation: An attempt was made to access a memory object which does\n" | 
|---|
| 571 | "             not belong to the current process. Most probable causes\n" | 
|---|
| 572 | "             for this are that an invalid pointer was used, there was\n" | 
|---|
| 573 | "             confusion with administering memory or error conditions \n" | 
|---|
| 574 | "             were not properly checked for.\n"); | 
|---|
| 575 | break; | 
|---|
| 576 |  | 
|---|
| 577 | case XCPT_INTEGER_DIVIDE_BY_ZERO: | 
|---|
| 578 | fprintf(file, "\nXCPT_INTEGER_DIVIDE_BY_ZERO.\n"); | 
|---|
| 579 | fprintf(file, | 
|---|
| 580 | "Explanation: An attempt was made to divide an integer value by zero,\n" | 
|---|
| 581 | "             which is not defined.\n"); | 
|---|
| 582 | break; | 
|---|
| 583 |  | 
|---|
| 584 | case XCPT_ILLEGAL_INSTRUCTION: | 
|---|
| 585 | fprintf(file, "\nXCPT_ILLEGAL_INSTRUCTION.\n"); | 
|---|
| 586 | fprintf(file, | 
|---|
| 587 | "Explanation: An attempt was made to execute an instruction that\n" | 
|---|
| 588 | "             is not defined on this machine's architecture.\n"); | 
|---|
| 589 | break; | 
|---|
| 590 |  | 
|---|
| 591 | case XCPT_PRIVILEGED_INSTRUCTION: | 
|---|
| 592 | fprintf(file, "\nXCPT_PRIVILEGED_INSTRUCTION.\n"); | 
|---|
| 593 | fprintf(file, | 
|---|
| 594 | "Explanation: An attempt was made to execute an instruction that\n" | 
|---|
| 595 | "             is not permitted in the current machine mode or that\n" | 
|---|
| 596 | "             the program had no permission to execute.\n"); | 
|---|
| 597 | break; | 
|---|
| 598 |  | 
|---|
| 599 | case XCPT_INTEGER_OVERFLOW: | 
|---|
| 600 | fprintf(file, "\nXCPT_INTEGER_OVERFLOW.\n"); | 
|---|
| 601 | fprintf(file, | 
|---|
| 602 | "Explanation: An integer operation generated a carry-out of the most\n" | 
|---|
| 603 | "             significant bit. This is a sign of an attempt to store\n" | 
|---|
| 604 | "             a value which does not fit into an integer variable.\n"); | 
|---|
| 605 | break; | 
|---|
| 606 |  | 
|---|
| 607 | default: | 
|---|
| 608 | fprintf(file, "\nUnknown OS/2 exception number %d.\n", pReportRec->ExceptionNum); | 
|---|
| 609 | fprintf(file, "Look this up in the OS/2 header files.\n"); | 
|---|
| 610 | break; | 
|---|
| 611 | } | 
|---|
| 612 |  | 
|---|
| 613 | // V0.9.16 (2001-11-02) [pr]: We already got this info. above - this overwrites the | 
|---|
| 614 | // original values before the priority change, which is rather confusing. | 
|---|
| 615 | // if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR) | 
|---|
| 616 | { | 
|---|
| 617 | /* | 
|---|
| 618 | * process info: | 
|---|
| 619 | * | 
|---|
| 620 | */ | 
|---|
| 621 |  | 
|---|
| 622 | if ((ptib) && (ppib))       // (99-11-01) [umoeller] | 
|---|
| 623 | { | 
|---|
| 624 | if (pContextRec->ContextFlags & CONTEXT_CONTROL) | 
|---|
| 625 | { | 
|---|
| 626 | // get the main module | 
|---|
| 627 | hMod1 = ppib->pib_hmte; | 
|---|
| 628 | DosQueryModuleName(hMod1, | 
|---|
| 629 | sizeof(szMod1), | 
|---|
| 630 | szMod1); | 
|---|
| 631 |  | 
|---|
| 632 | // get the trapping module | 
|---|
| 633 | DosQueryModFromEIP(&hMod2, | 
|---|
| 634 | &ulObjNum, | 
|---|
| 635 | sizeof(szMod2), | 
|---|
| 636 | szMod2, | 
|---|
| 637 | &ulOffset, | 
|---|
| 638 | pContextRec->ctx_RegEip); | 
|---|
| 639 | DosQueryModuleName(hMod2, | 
|---|
| 640 | sizeof(szMod2), | 
|---|
| 641 | szMod2); | 
|---|
| 642 | } | 
|---|
| 643 |  | 
|---|
| 644 | fprintf(file, | 
|---|
| 645 | "\nProcess information:" | 
|---|
| 646 | "\n    Process ID:      0x%lX" | 
|---|
| 647 | "\n    Process module:  0x%lX (%s)" | 
|---|
| 648 | "\n    Trapping module: 0x%lX (%s)" | 
|---|
| 649 | "\n    Object: %ld\n",  // V0.9.16 (2001-11-02) [pr]: make this display signed | 
|---|
| 650 | ppib->pib_ulpid, | 
|---|
| 651 | hMod1, szMod1, | 
|---|
| 652 | hMod2, szMod2, | 
|---|
| 653 | ulObjNum); | 
|---|
| 654 |  | 
|---|
| 655 | fprintf(file, | 
|---|
| 656 | "\nTrapping thread information:" | 
|---|
| 657 | "\n    Thread ID:       0x%lX (%lu)" | 
|---|
| 658 | "\n    Thread slot ID:  0x%lX (%lu)"        // added V0.9.19 (2002-03-28) [umoeller] | 
|---|
| 659 | "\n    Priority:        0x%lX\n", | 
|---|
| 660 | ptib->tib_ptib2->tib2_ultid, ptib->tib_ptib2->tib2_ultid, | 
|---|
| 661 | ptib->tib_ordinal, ptib->tib_ordinal, | 
|---|
| 662 | ulOldPriority); | 
|---|
| 663 | } | 
|---|
| 664 | else | 
|---|
| 665 | fprintf(file, "\nProcess information was not available."); | 
|---|
| 666 |  | 
|---|
| 667 | /* | 
|---|
| 668 | *  now call the hook, if one has been defined, | 
|---|
| 669 | *  so that the application can write additional | 
|---|
| 670 | *  information to the traplog (V0.9.0) | 
|---|
| 671 | */ | 
|---|
| 672 |  | 
|---|
| 673 | if (G_pfnExcHook) | 
|---|
| 674 | G_pfnExcHook(file, ptib, ulOldPriority);  // V0.9.16 (2001-12-02) [pr] | 
|---|
| 675 |  | 
|---|
| 676 | // *** registers | 
|---|
| 677 |  | 
|---|
| 678 | fprintf(file, "\nRegisters:"); | 
|---|
| 679 | if (pContextRec->ContextFlags & CONTEXT_INTEGER) | 
|---|
| 680 | { | 
|---|
| 681 | // DS the following 4 added V0.9.6 (2000-11-06) [umoeller] | 
|---|
| 682 | fprintf(file, "\n    DS  = %08lX  ", pContextRec->ctx_SegDs); | 
|---|
| 683 | excDescribePage(file, pContextRec->ctx_SegDs); | 
|---|
| 684 | // ES | 
|---|
| 685 | fprintf(file, "\n    ES  = %08lX  ", pContextRec->ctx_SegEs); | 
|---|
| 686 | excDescribePage(file, pContextRec->ctx_SegEs); | 
|---|
| 687 | // FS | 
|---|
| 688 | fprintf(file, "\n    FS  = %08lX  ", pContextRec->ctx_SegFs); | 
|---|
| 689 | excDescribePage(file, pContextRec->ctx_SegFs); | 
|---|
| 690 | // GS | 
|---|
| 691 | fprintf(file, "\n    GS  = %08lX  ", pContextRec->ctx_SegGs); | 
|---|
| 692 | excDescribePage(file, pContextRec->ctx_SegGs); | 
|---|
| 693 |  | 
|---|
| 694 | // EAX | 
|---|
| 695 | fprintf(file, "\n    EAX = %08lX  ", pContextRec->ctx_RegEax); | 
|---|
| 696 | excDescribePage(file, pContextRec->ctx_RegEax); | 
|---|
| 697 | // EBX | 
|---|
| 698 | fprintf(file, "\n    EBX = %08lX  ", pContextRec->ctx_RegEbx); | 
|---|
| 699 | excDescribePage(file, pContextRec->ctx_RegEbx); | 
|---|
| 700 | // ECX | 
|---|
| 701 | fprintf(file, "\n    ECX = %08lX  ", pContextRec->ctx_RegEcx); | 
|---|
| 702 | excDescribePage(file, pContextRec->ctx_RegEcx); | 
|---|
| 703 | // EDX | 
|---|
| 704 | fprintf(file, "\n    EDX = %08lX  ", pContextRec->ctx_RegEdx); | 
|---|
| 705 | excDescribePage(file, pContextRec->ctx_RegEdx); | 
|---|
| 706 | // ESI | 
|---|
| 707 | fprintf(file, "\n    ESI = %08lX  ", pContextRec->ctx_RegEsi); | 
|---|
| 708 | excDescribePage(file, pContextRec->ctx_RegEsi); | 
|---|
| 709 | // EDI | 
|---|
| 710 | fprintf(file, "\n    EDI = %08lX  ", pContextRec->ctx_RegEdi); | 
|---|
| 711 | excDescribePage(file, pContextRec->ctx_RegEdi); | 
|---|
| 712 | fprintf(file, "\n"); | 
|---|
| 713 | } | 
|---|
| 714 | else | 
|---|
| 715 | fprintf(file, " not available\n"); | 
|---|
| 716 |  | 
|---|
| 717 | if (pContextRec->ContextFlags & CONTEXT_CONTROL) | 
|---|
| 718 | { | 
|---|
| 719 |  | 
|---|
| 720 | // *** instruction | 
|---|
| 721 |  | 
|---|
| 722 | fprintf(file, "Instruction pointer (where exception occurred):\n    CS:EIP = %04lX:%08lX  ", | 
|---|
| 723 | pContextRec->ctx_SegCs, | 
|---|
| 724 | pContextRec->ctx_RegEip); | 
|---|
| 725 | excDescribePage(file, pContextRec->ctx_RegEip); | 
|---|
| 726 |  | 
|---|
| 727 | // *** CPU flags | 
|---|
| 728 |  | 
|---|
| 729 | fprintf(file, "\n    EFLAGS = %08lX", pContextRec->ctx_EFlags); | 
|---|
| 730 |  | 
|---|
| 731 | /* | 
|---|
| 732 | * stack: | 
|---|
| 733 | * | 
|---|
| 734 | */ | 
|---|
| 735 |  | 
|---|
| 736 | fprintf(file, "\nStack:\n    Base:         %08lX\n    Limit:        %08lX", | 
|---|
| 737 | (ULONG)(ptib ? ptib->tib_pstack : 0), | 
|---|
| 738 | (ULONG)(ptib ? ptib->tib_pstacklimit : 0)); | 
|---|
| 739 | fprintf(file, "\n    SS:ESP = %04lX:%08lX  ", | 
|---|
| 740 | pContextRec->ctx_SegSs, | 
|---|
| 741 | pContextRec->ctx_RegEsp); | 
|---|
| 742 | excDescribePage(file, pContextRec->ctx_RegEsp); | 
|---|
| 743 |  | 
|---|
| 744 | fprintf(file, "\n    EBP    =      %08lX  ", pContextRec->ctx_RegEbp); | 
|---|
| 745 | excDescribePage(file, pContextRec->ctx_RegEbp); | 
|---|
| 746 |  | 
|---|
| 747 | /* | 
|---|
| 748 | * stack dump: | 
|---|
| 749 | */ | 
|---|
| 750 |  | 
|---|
| 751 | if (ptib != 0) | 
|---|
| 752 | { | 
|---|
| 753 | excDumpStackFrames(file, ptib, pContextRec); | 
|---|
| 754 | } | 
|---|
| 755 | } | 
|---|
| 756 | } | 
|---|
| 757 | fprintf(file, "\n"); | 
|---|
| 758 |  | 
|---|
| 759 | // reset old priority | 
|---|
| 760 | DosSetPriority(PRTYS_THREAD, | 
|---|
| 761 | (ulOldPriority & 0x0F00) >> 8, | 
|---|
| 762 | (UCHAR)ulOldPriority, | 
|---|
| 763 | 0);     // current thread | 
|---|
| 764 |  | 
|---|
| 765 | // lower global flag again V0.9.13 (2001-06-19) [umoeller] | 
|---|
| 766 | G_ulExplainExceptionRunning--; | 
|---|
| 767 | } | 
|---|
| 768 |  | 
|---|
| 769 | /* ****************************************************************** | 
|---|
| 770 | * | 
|---|
| 771 | *   Exported routines | 
|---|
| 772 | * | 
|---|
| 773 | ********************************************************************/ | 
|---|
| 774 |  | 
|---|
| 775 | /* | 
|---|
| 776 | *@@ excRegisterHooks: | 
|---|
| 777 | *      this registers hooks which get called for | 
|---|
| 778 | *      exception handlers. You can set any of the | 
|---|
| 779 | *      hooks to NULL for safe defaults (see top of | 
|---|
| 780 | *      except.c for details). You can set none, | 
|---|
| 781 | *      one, or both of the hooks, and you can call | 
|---|
| 782 | *      this function several times. | 
|---|
| 783 | * | 
|---|
| 784 | *      Both hooks get called whenever an exception | 
|---|
| 785 | *      occurs, so there better be no bugs in these | 
|---|
| 786 | *      routines. ;-) They only get called from | 
|---|
| 787 | *      within excHandlerLoud (because excHandlerQuiet | 
|---|
| 788 | *      writes no trap logs). | 
|---|
| 789 | * | 
|---|
| 790 | *      Registering hooks affects all threads that use | 
|---|
| 791 | *      the exception handlers. | 
|---|
| 792 | * | 
|---|
| 793 | *      The hooks are as follows: | 
|---|
| 794 | * | 
|---|
| 795 | *      --  pfnExcOpenFileNew gets called to open | 
|---|
| 796 | *          the trap log file. This can return a FILE* | 
|---|
| 797 | *          pointer from fopen() (which will be closed | 
|---|
| 798 | *          automatically by the handler). | 
|---|
| 799 | * | 
|---|
| 800 | *          If this hook is not defined, ?:\TRAP.LOG is | 
|---|
| 801 | *          used. Use this hook to specify a different file | 
|---|
| 802 | *          and have some notes written into it before the | 
|---|
| 803 | *          actual exception info. | 
|---|
| 804 | * | 
|---|
| 805 | *          If the hook returns a null FILE* pointer, | 
|---|
| 806 | *          logging is disabled, and the "loud" handler | 
|---|
| 807 | *          effectively behaves like the "quiet" handler. | 
|---|
| 808 | * | 
|---|
| 809 | *      --  pfnExcHookNew gets called while the trap log | 
|---|
| 810 | *          is being written. At this point, | 
|---|
| 811 | *          the following info has been written into | 
|---|
| 812 | *          the trap log already: | 
|---|
| 813 | * | 
|---|
| 814 | *          -- exception type/address block | 
|---|
| 815 | * | 
|---|
| 816 | *          -- exception explanation | 
|---|
| 817 | * | 
|---|
| 818 | *          -- process information | 
|---|
| 819 | * | 
|---|
| 820 | *          _After_ the hook, the exception handler | 
|---|
| 821 | *          continues with the "Registers" information | 
|---|
| 822 | *          and stack dump/analysis. | 
|---|
| 823 | * | 
|---|
| 824 | *          Use this hook to write additional application | 
|---|
| 825 | *          info into the trap log, such as the state | 
|---|
| 826 | *          of your own threads and mutexes. | 
|---|
| 827 | * | 
|---|
| 828 | *      --  pfnExcHookError gets called when the TRY_* macros | 
|---|
| 829 | *          fail to install an exception handler (when | 
|---|
| 830 | *          DosSetExceptionHandler fails). I've never seen | 
|---|
| 831 | *          this happen. | 
|---|
| 832 | * | 
|---|
| 833 | *@@added V0.9.0 [umoeller] | 
|---|
| 834 | *@@changed V0.9.2 (2000-03-10) [umoeller]: pfnExcHookError added | 
|---|
| 835 | */ | 
|---|
| 836 |  | 
|---|
| 837 | VOID excRegisterHooks(PFNEXCOPENFILE pfnExcOpenFileNew, | 
|---|
| 838 | PFNEXCHOOK pfnExcHookNew, | 
|---|
| 839 | PFNEXCHOOKERROR pfnExcHookError, | 
|---|
| 840 | BOOL fBeepOnExceptionNew) | 
|---|
| 841 | { | 
|---|
| 842 | // adjust the global variables | 
|---|
| 843 | G_pfnExcOpenFile = pfnExcOpenFileNew; | 
|---|
| 844 | G_pfnExcHook = pfnExcHookNew; | 
|---|
| 845 | G_pfnExcHookError = pfnExcHookError; | 
|---|
| 846 | G_fBeepOnException = fBeepOnExceptionNew; | 
|---|
| 847 | } | 
|---|
| 848 |  | 
|---|
| 849 | /* | 
|---|
| 850 | *@@ excHandlerLoud: | 
|---|
| 851 | *      this is the "sophisticated" exception handler; | 
|---|
| 852 | *      which gives forth a loud sequence of beeps thru the | 
|---|
| 853 | *      speaker, writes a trap log and then returns back | 
|---|
| 854 | *      to the thread to continue execution, i.e. the | 
|---|
| 855 | *      default OS/2 exception handler will never get | 
|---|
| 856 | *      called. | 
|---|
| 857 | * | 
|---|
| 858 | *      This requires a setjmp() call on | 
|---|
| 859 | *      EXCEPTIONREGISTRATIONRECORD2.jmpThread before | 
|---|
| 860 | *      being installed. The TRY_LOUD macro will take | 
|---|
| 861 | *      care of this for you (see except.c). | 
|---|
| 862 | * | 
|---|
| 863 | *      This intercepts the following exceptions (see | 
|---|
| 864 | *      the OS/2 Control Program Reference for details): | 
|---|
| 865 | * | 
|---|
| 866 | *      --  XCPT_ACCESS_VIOLATION         (traps 0x0d, 0x0e) | 
|---|
| 867 | *      --  XCPT_INTEGER_DIVIDE_BY_ZERO   (trap 0) | 
|---|
| 868 | *      --  XCPT_ILLEGAL_INSTRUCTION      (trap 6) | 
|---|
| 869 | *      --  XCPT_PRIVILEGED_INSTRUCTION | 
|---|
| 870 | *      --  XCPT_INTEGER_OVERFLOW         (trap 4) | 
|---|
| 871 | * | 
|---|
| 872 | *      For these exceptions, we call the functions in debug.c | 
|---|
| 873 | *      to try to find debug code or SYM file information about | 
|---|
| 874 | *      what source code corresponds to the error. | 
|---|
| 875 | * | 
|---|
| 876 | *      See excRegisterHooks for the default setup of this. | 
|---|
| 877 | * | 
|---|
| 878 | *      Note that to get meaningful debugging information | 
|---|
| 879 | *      in this handler's traplog, you need the following: | 
|---|
| 880 | * | 
|---|
| 881 | *      a)  have a MAP file created at link time (/MAP) | 
|---|
| 882 | * | 
|---|
| 883 | *      b)  convert the MAP to a SYM file using MAPSYM | 
|---|
| 884 | * | 
|---|
| 885 | *      c)  put the SYM file in the same directory of | 
|---|
| 886 | *          the module (EXE or DLL). This must have the | 
|---|
| 887 | *          same filestem as the module. | 
|---|
| 888 | * | 
|---|
| 889 | *      All other exceptions are passed to the next handler | 
|---|
| 890 | *      in the exception handler chain. This might be the | 
|---|
| 891 | *      C/C++ compiler handler or the default OS/2 handler, | 
|---|
| 892 | *      which will probably terminate the process. | 
|---|
| 893 | * | 
|---|
| 894 | *@@changed V0.9.0 [umoeller]: added support for thread termination | 
|---|
| 895 | *@@changed V0.9.2 (2000-03-10) [umoeller]: switched date format to ISO | 
|---|
| 896 | *@@changed V0.9.19 (2002-05-07) [umoeller]: added EXCEPTIONREPORTRECORD info so that catch block can check that | 
|---|
| 897 | */ | 
|---|
| 898 |  | 
|---|
| 899 | ULONG _System excHandlerLoud(PEXCEPTIONREPORTRECORD pReportRec, | 
|---|
| 900 | PEXCEPTIONREGISTRATIONRECORD2 pRegRec2, | 
|---|
| 901 | PCONTEXTRECORD pContextRec, | 
|---|
| 902 | PVOID pv) | 
|---|
| 903 | { | 
|---|
| 904 | /* From the VAC++3 docs: | 
|---|
| 905 | *      "The first thing an exception handler should do is check the | 
|---|
| 906 | *      exception flags. If EH_EXIT_UNWIND is set, meaning | 
|---|
| 907 | *      the thread is ending, the handler tells the operating system | 
|---|
| 908 | *      to pass the exception to the next exception handler. It does the | 
|---|
| 909 | *      same if the EH_UNWINDING flag is set, the flag that indicates | 
|---|
| 910 | *      this exception handler is being removed. | 
|---|
| 911 | *      The EH_NESTED_CALL flag indicates whether the exception | 
|---|
| 912 | *      occurred within an exception handler. If the handler does | 
|---|
| 913 | *      not check this flag, recursive exceptions could occur until | 
|---|
| 914 | *      there is no stack remaining." | 
|---|
| 915 | * | 
|---|
| 916 | *      So for all these conditions, we exit immediately. | 
|---|
| 917 | */ | 
|---|
| 918 |  | 
|---|
| 919 | // XWP V1.0.4 (2005-10-09) [pr]: Optimize | 
|---|
| 920 | if (pReportRec->fHandlerFlags & (EH_EXIT_UNWIND | EH_UNWINDING | EH_NESTED_CALL)) | 
|---|
| 921 | return XCPT_CONTINUE_SEARCH; | 
|---|
| 922 |  | 
|---|
| 923 | switch (pReportRec->ExceptionNum) | 
|---|
| 924 | { | 
|---|
| 925 | case XCPT_ACCESS_VIOLATION: | 
|---|
| 926 | case XCPT_INTEGER_DIVIDE_BY_ZERO: | 
|---|
| 927 | case XCPT_ILLEGAL_INSTRUCTION: | 
|---|
| 928 | case XCPT_PRIVILEGED_INSTRUCTION: | 
|---|
| 929 | case XCPT_INVALID_LOCK_SEQUENCE: | 
|---|
| 930 | case XCPT_INTEGER_OVERFLOW: | 
|---|
| 931 | { | 
|---|
| 932 | // "real" exceptions: | 
|---|
| 933 | FILE *file = NULL; | 
|---|
| 934 |  | 
|---|
| 935 | // open traplog file; | 
|---|
| 936 | if (G_pfnExcOpenFile) | 
|---|
| 937 | // hook defined for this: call it | 
|---|
| 938 | file = G_pfnExcOpenFile(); | 
|---|
| 939 | else | 
|---|
| 940 | { | 
|---|
| 941 | CHAR szFileName[100]; | 
|---|
| 942 | // no hook defined: open some | 
|---|
| 943 | // default traplog file in root directory of | 
|---|
| 944 | // boot drive | 
|---|
| 945 | sprintf(szFileName, "%c:\\trap.log", doshQueryBootDrive()); | 
|---|
| 946 |  | 
|---|
| 947 | if (file = fopen(szFileName, "a")) | 
|---|
| 948 | { | 
|---|
| 949 | DATETIME DT; | 
|---|
| 950 | DosGetDateTime(&DT); | 
|---|
| 951 | fprintf(file, | 
|---|
| 952 | "\nTrap message -- Date: %04d-%02d-%02d, Time: %02d:%02d:%02d\n", | 
|---|
| 953 | DT.year, DT.month, DT.day, | 
|---|
| 954 | DT.hours, DT.minutes, DT.seconds); | 
|---|
| 955 | fprintf(file, "------------------------------------------------\n"); | 
|---|
| 956 |  | 
|---|
| 957 | } | 
|---|
| 958 | } | 
|---|
| 959 |  | 
|---|
| 960 | if (file) | 
|---|
| 961 | { | 
|---|
| 962 | // write error log | 
|---|
| 963 | excExplainException(file, | 
|---|
| 964 | "excHandlerLoud", | 
|---|
| 965 | pReportRec, | 
|---|
| 966 | pContextRec); | 
|---|
| 967 | fclose(file); | 
|---|
| 968 | } | 
|---|
| 969 |  | 
|---|
| 970 | // copy report rec to user buffer | 
|---|
| 971 | // V0.9.19 (2002-05-07) [umoeller] | 
|---|
| 972 | memcpy(&pRegRec2->err, | 
|---|
| 973 | pReportRec, | 
|---|
| 974 | sizeof(EXCEPTIONREPORTRECORD)); | 
|---|
| 975 |  | 
|---|
| 976 | // jump back to failing routine | 
|---|
| 977 | longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum); | 
|---|
| 978 | } | 
|---|
| 979 | break; | 
|---|
| 980 | } | 
|---|
| 981 |  | 
|---|
| 982 | // not handled | 
|---|
| 983 | return XCPT_CONTINUE_SEARCH; | 
|---|
| 984 | } | 
|---|
| 985 |  | 
|---|
| 986 | /* | 
|---|
| 987 | *@@ excHandlerQuiet: | 
|---|
| 988 | *      "quiet" xcpt handler, which simply suppresses exceptions; | 
|---|
| 989 | *      this is useful for certain error-prone functions, where | 
|---|
| 990 | *      exceptions are likely to appear, for example used by | 
|---|
| 991 | *      wpshCheckObject to implement a fail-safe SOM object check. | 
|---|
| 992 | * | 
|---|
| 993 | *      This does _not_ write an error log and makes _no_ sound. | 
|---|
| 994 | *      This simply jumps back to the trapping thread or | 
|---|
| 995 | *      calls EXCEPTIONREGISTRATIONRECORD2.pfnOnKill. | 
|---|
| 996 | * | 
|---|
| 997 | *      Other than that, this behaves like excHandlerLoud. | 
|---|
| 998 | * | 
|---|
| 999 | *      This is best registered thru the TRY_QUIET macro | 
|---|
| 1000 | *      (new with V0.84, described in except.c), which | 
|---|
| 1001 | *      does the necessary setup. | 
|---|
| 1002 | * | 
|---|
| 1003 | *@@changed V0.9.0 [umoeller]: added support for thread termination | 
|---|
| 1004 | *@@changed V0.9.19 (2002-05-07) [umoeller]: added EXCEPTIONREPORTRECORD info so that catch block can check that | 
|---|
| 1005 | */ | 
|---|
| 1006 |  | 
|---|
| 1007 | ULONG _System excHandlerQuiet(PEXCEPTIONREPORTRECORD pReportRec, | 
|---|
| 1008 | PEXCEPTIONREGISTRATIONRECORD2 pRegRec2, | 
|---|
| 1009 | PCONTEXTRECORD pContextRec, | 
|---|
| 1010 | PVOID pv) | 
|---|
| 1011 | { | 
|---|
| 1012 | // XWP V1.0.4 (2005-10-09) [pr]: Optimize | 
|---|
| 1013 | if (pReportRec->fHandlerFlags & (EH_EXIT_UNWIND | EH_UNWINDING | EH_NESTED_CALL)) | 
|---|
| 1014 | return XCPT_CONTINUE_SEARCH; | 
|---|
| 1015 |  | 
|---|
| 1016 | switch (pReportRec->ExceptionNum) | 
|---|
| 1017 | { | 
|---|
| 1018 | case XCPT_ACCESS_VIOLATION: | 
|---|
| 1019 | case XCPT_INTEGER_DIVIDE_BY_ZERO: | 
|---|
| 1020 | case XCPT_ILLEGAL_INSTRUCTION: | 
|---|
| 1021 | case XCPT_PRIVILEGED_INSTRUCTION: | 
|---|
| 1022 | case XCPT_INVALID_LOCK_SEQUENCE: | 
|---|
| 1023 | case XCPT_INTEGER_OVERFLOW: | 
|---|
| 1024 | // write excpt explanation only if the | 
|---|
| 1025 | // resp. debugging #define is set (setup.h) | 
|---|
| 1026 | #ifdef DEBUG_WRITEQUIETEXCPT | 
|---|
| 1027 | { | 
|---|
| 1028 | FILE *file = excOpenTraplogFile(); | 
|---|
| 1029 | excExplainException(file, | 
|---|
| 1030 | "excHandlerQuiet", | 
|---|
| 1031 | pReportRec, | 
|---|
| 1032 | pContextRec); | 
|---|
| 1033 | fclose(file); | 
|---|
| 1034 | } | 
|---|
| 1035 | #endif | 
|---|
| 1036 |  | 
|---|
| 1037 | // copy report rec to user buffer | 
|---|
| 1038 | // V0.9.19 (2002-05-07) [umoeller] | 
|---|
| 1039 | memcpy(&pRegRec2->err, | 
|---|
| 1040 | pReportRec, | 
|---|
| 1041 | sizeof(EXCEPTIONREPORTRECORD)); | 
|---|
| 1042 |  | 
|---|
| 1043 | // jump back to failing routine | 
|---|
| 1044 | longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum); | 
|---|
| 1045 | break; | 
|---|
| 1046 | } | 
|---|
| 1047 |  | 
|---|
| 1048 | return XCPT_CONTINUE_SEARCH; | 
|---|
| 1049 | } | 
|---|
| 1050 |  | 
|---|
| 1051 |  | 
|---|