| 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 | + 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-99 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 | if (pReportRec->fHandlerFlags & EH_EXIT_UNWIND)
|
|---|
| 920 | return XCPT_CONTINUE_SEARCH;
|
|---|
| 921 | if (pReportRec->fHandlerFlags & EH_UNWINDING)
|
|---|
| 922 | return XCPT_CONTINUE_SEARCH;
|
|---|
| 923 | if (pReportRec->fHandlerFlags & EH_NESTED_CALL)
|
|---|
| 924 | return XCPT_CONTINUE_SEARCH;
|
|---|
| 925 |
|
|---|
| 926 | switch (pReportRec->ExceptionNum)
|
|---|
| 927 | {
|
|---|
| 928 | case XCPT_ACCESS_VIOLATION:
|
|---|
| 929 | case XCPT_INTEGER_DIVIDE_BY_ZERO:
|
|---|
| 930 | case XCPT_ILLEGAL_INSTRUCTION:
|
|---|
| 931 | case XCPT_PRIVILEGED_INSTRUCTION:
|
|---|
| 932 | case XCPT_INVALID_LOCK_SEQUENCE:
|
|---|
| 933 | case XCPT_INTEGER_OVERFLOW:
|
|---|
| 934 | {
|
|---|
| 935 | // "real" exceptions:
|
|---|
| 936 | FILE *file = NULL;
|
|---|
| 937 |
|
|---|
| 938 | // open traplog file;
|
|---|
| 939 | if (G_pfnExcOpenFile)
|
|---|
| 940 | // hook defined for this: call it
|
|---|
| 941 | file = G_pfnExcOpenFile();
|
|---|
| 942 | else
|
|---|
| 943 | {
|
|---|
| 944 | CHAR szFileName[100];
|
|---|
| 945 | // no hook defined: open some
|
|---|
| 946 | // default traplog file in root directory of
|
|---|
| 947 | // boot drive
|
|---|
| 948 | sprintf(szFileName, "%c:\\trap.log", doshQueryBootDrive());
|
|---|
| 949 |
|
|---|
| 950 | if (file = fopen(szFileName, "a"))
|
|---|
| 951 | {
|
|---|
| 952 | DATETIME DT;
|
|---|
| 953 | DosGetDateTime(&DT);
|
|---|
| 954 | fprintf(file,
|
|---|
| 955 | "\nTrap message -- Date: %04d-%02d-%02d, Time: %02d:%02d:%02d\n",
|
|---|
| 956 | DT.year, DT.month, DT.day,
|
|---|
| 957 | DT.hours, DT.minutes, DT.seconds);
|
|---|
| 958 | fprintf(file, "------------------------------------------------\n");
|
|---|
| 959 |
|
|---|
| 960 | }
|
|---|
| 961 | }
|
|---|
| 962 |
|
|---|
| 963 | if (file)
|
|---|
| 964 | {
|
|---|
| 965 | // write error log
|
|---|
| 966 | excExplainException(file,
|
|---|
| 967 | "excHandlerLoud",
|
|---|
| 968 | pReportRec,
|
|---|
| 969 | pContextRec);
|
|---|
| 970 | fclose(file);
|
|---|
| 971 | }
|
|---|
| 972 |
|
|---|
| 973 | // copy report rec to user buffer
|
|---|
| 974 | // V0.9.19 (2002-05-07) [umoeller]
|
|---|
| 975 | memcpy(&pRegRec2->err,
|
|---|
| 976 | pReportRec,
|
|---|
| 977 | sizeof(EXCEPTIONREPORTRECORD));
|
|---|
| 978 |
|
|---|
| 979 | // jump back to failing routine
|
|---|
| 980 | longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum);
|
|---|
| 981 | }
|
|---|
| 982 | break;
|
|---|
| 983 | }
|
|---|
| 984 |
|
|---|
| 985 | // not handled
|
|---|
| 986 | return XCPT_CONTINUE_SEARCH;
|
|---|
| 987 | }
|
|---|
| 988 |
|
|---|
| 989 | /*
|
|---|
| 990 | *@@ excHandlerQuiet:
|
|---|
| 991 | * "quiet" xcpt handler, which simply suppresses exceptions;
|
|---|
| 992 | * this is useful for certain error-prone functions, where
|
|---|
| 993 | * exceptions are likely to appear, for example used by
|
|---|
| 994 | * wpshCheckObject to implement a fail-safe SOM object check.
|
|---|
| 995 | *
|
|---|
| 996 | * This does _not_ write an error log and makes _no_ sound.
|
|---|
| 997 | * This simply jumps back to the trapping thread or
|
|---|
| 998 | * calls EXCEPTIONREGISTRATIONRECORD2.pfnOnKill.
|
|---|
| 999 | *
|
|---|
| 1000 | * Other than that, this behaves like excHandlerLoud.
|
|---|
| 1001 | *
|
|---|
| 1002 | * This is best registered thru the TRY_QUIET macro
|
|---|
| 1003 | * (new with V0.84, described in except.c), which
|
|---|
| 1004 | * does the necessary setup.
|
|---|
| 1005 | *
|
|---|
| 1006 | *@@changed V0.9.0 [umoeller]: added support for thread termination
|
|---|
| 1007 | *@@changed V0.9.19 (2002-05-07) [umoeller]: added EXCEPTIONREPORTRECORD info so that catch block can check that
|
|---|
| 1008 | */
|
|---|
| 1009 |
|
|---|
| 1010 | ULONG _System excHandlerQuiet(PEXCEPTIONREPORTRECORD pReportRec,
|
|---|
| 1011 | PEXCEPTIONREGISTRATIONRECORD2 pRegRec2,
|
|---|
| 1012 | PCONTEXTRECORD pContextRec,
|
|---|
| 1013 | PVOID pv)
|
|---|
| 1014 | {
|
|---|
| 1015 | if (pReportRec->fHandlerFlags & EH_EXIT_UNWIND)
|
|---|
| 1016 | return XCPT_CONTINUE_SEARCH;
|
|---|
| 1017 | if (pReportRec->fHandlerFlags & EH_UNWINDING)
|
|---|
| 1018 | return XCPT_CONTINUE_SEARCH;
|
|---|
| 1019 | if (pReportRec->fHandlerFlags & EH_NESTED_CALL)
|
|---|
| 1020 | return XCPT_CONTINUE_SEARCH;
|
|---|
| 1021 |
|
|---|
| 1022 | switch (pReportRec->ExceptionNum)
|
|---|
| 1023 | {
|
|---|
| 1024 | case XCPT_ACCESS_VIOLATION:
|
|---|
| 1025 | case XCPT_INTEGER_DIVIDE_BY_ZERO:
|
|---|
| 1026 | case XCPT_ILLEGAL_INSTRUCTION:
|
|---|
| 1027 | case XCPT_PRIVILEGED_INSTRUCTION:
|
|---|
| 1028 | case XCPT_INVALID_LOCK_SEQUENCE:
|
|---|
| 1029 | case XCPT_INTEGER_OVERFLOW:
|
|---|
| 1030 | // write excpt explanation only if the
|
|---|
| 1031 | // resp. debugging #define is set (setup.h)
|
|---|
| 1032 | #ifdef DEBUG_WRITEQUIETEXCPT
|
|---|
| 1033 | {
|
|---|
| 1034 | FILE *file = excOpenTraplogFile();
|
|---|
| 1035 | excExplainException(file,
|
|---|
| 1036 | "excHandlerQuiet",
|
|---|
| 1037 | pReportRec,
|
|---|
| 1038 | pContextRec);
|
|---|
| 1039 | fclose(file);
|
|---|
| 1040 | }
|
|---|
| 1041 | #endif
|
|---|
| 1042 |
|
|---|
| 1043 | // copy report rec to user buffer
|
|---|
| 1044 | // V0.9.19 (2002-05-07) [umoeller]
|
|---|
| 1045 | memcpy(&pRegRec2->err,
|
|---|
| 1046 | pReportRec,
|
|---|
| 1047 | sizeof(EXCEPTIONREPORTRECORD));
|
|---|
| 1048 |
|
|---|
| 1049 | // jump back to failing routine
|
|---|
| 1050 | longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum);
|
|---|
| 1051 | break;
|
|---|
| 1052 | }
|
|---|
| 1053 |
|
|---|
| 1054 | return XCPT_CONTINUE_SEARCH;
|
|---|
| 1055 | }
|
|---|
| 1056 |
|
|---|
| 1057 |
|
|---|