source: branches/branch-1-0/src/helpers/except.c@ 445

Last change on this file since 445 was 431, checked in by rlwalsh, 8 years ago

exception handler: update exception report to show function that set
the handler; optionally, print a debug message with up to 4 arguments

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 40.1 KB
Line 
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 M”ller.
182 *
183 * Note: Version numbering in this file relates to XWorkplace version
184 * numbering.
185 *
186 *@@header "helpers\except.h"
187 */
188
189/*
190 * This file Copyright (C) 1992-2010 Ulrich M”ller,
191 * Monte Copeland,
192 * Roman Stangl,
193 * Kim Rasmussen,
194 * Marc Fiammante,
195 * John Currier,
196 * Anthony Cruise.
197 * This file is part of the "XWorkplace helpers" source package.
198 * This is free software; you can redistribute it and/or modify
199 * it under the terms of the GNU General Public License as published
200 * by the Free Software Foundation, in version 2 as it comes in the
201 * "COPYING" file of the XWorkplace main distribution.
202 * This program is distributed in the hope that it will be useful,
203 * but WITHOUT ANY WARRANTY; without even the implied warranty of
204 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
205 * GNU General Public License for more details.
206 */
207
208#define OS2EMX_PLAIN_CHAR
209 // this is needed for "os2emx.h"; if this is defined,
210 // emx will define PSZ as _signed_ char, otherwise
211 // as unsigned char
212
213#define INCL_DOSMODULEMGR
214#define INCL_DOSEXCEPTIONS
215#define INCL_DOSPROCESS
216#define INCL_DOSMISC
217#define INCL_DOSERRORS
218#include <os2.h>
219
220// C library headers
221#include <stdio.h> // needed for except.h
222#include <stdlib.h>
223#include <time.h>
224#include <string.h>
225#include <setjmp.h> // needed for except.h
226#include <assert.h> // needed for except.h
227
228#define DONT_REPLACE_MALLOC
229#include "setup.h" // code generation and debugging options
230
231// headers in /helpers
232#include "helpers\dosh.h" // Control Program helper routines
233#include "helpers\except.h" // exception handling
234#include "helpers\debug.h" // symbol/debug code analysis
235
236#pragma hdrstop
237
238/* ******************************************************************
239 *
240 * Global variables
241 *
242 ********************************************************************/
243
244// hooks to be registered using excRegisterHooks
245PFNEXCOPENFILE G_pfnExcOpenFile = 0;
246PFNEXCHOOK G_pfnExcHook = 0;
247PFNEXCHOOKERROR G_pfnExcHookError = 0;
248// beep flag for excHandlerLoud
249BOOL G_fBeepOnException = TRUE;
250
251ULONG 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
274VOID 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 {
303 if (ulCheck < 0x10000)
304 fprintf(file, "not an address");
305 else
306 fprintf(file, "invalid address");
307 }
308}
309
310/*
311 *@@ excPrintStackFrame:
312 * wrapper for dbgPrintStackFrame to format
313 * output stuff right.
314 *
315 *@@added V0.9.2 (2000-03-10) [umoeller]
316 *@@changed V0.9.12 (2001-05-12) [umoeller]: added seg:ofs to output always
317 */
318
319VOID excPrintStackFrame(FILE *file, // in: output log file
320 PSZ pszDescription, // in: description for stack frame (should be eight chars)
321 ULONG ulAddress) // in: address to debug
322{
323 APIRET arc = NO_ERROR;
324 HMODULE hmod1 = NULLHANDLE;
325 CHAR szMod1[2*CCHMAXPATH] = "unknown";
326 ULONG ulObject = 0,
327 ulOffset = 0;
328 fprintf(file,
329 " %-8s: %08lX ",
330 pszDescription,
331 ulAddress);
332 arc = DosQueryModFromEIP(&hmod1,
333 &ulObject,
334 sizeof(szMod1), szMod1,
335 &ulOffset,
336 ulAddress);
337
338 if (arc != NO_ERROR)
339 {
340 // error:
341 fprintf(file,
342 " %-8s Error: DosQueryModFromEIP returned %lu\n",
343 szMod1,
344 arc);
345 }
346 else
347 {
348 CHAR szFullName[2*CCHMAXPATH] = "unknown";
349
350 fprintf(file,
351 " %-8s %02lX:%08lX\n ",
352 szMod1,
353 ulObject + 1, // V0.9.12 (2001-05-12) [umoeller]
354 ulOffset); // V0.9.12 (2001-05-12) [umoeller]
355
356 DosQueryModuleName(hmod1, sizeof(szFullName), szFullName);
357 dbgPrintStackFrame(file,
358 szFullName,
359 ulObject,
360 ulOffset);
361
362 fprintf(file, "\n");
363
364 // make a 'tick' sound to let the user know we're still alive
365 DosBeep(2000, 10);
366 }
367}
368
369/*
370 *@@ excDumpStackFrames:
371 * called from excExplainException to dump the
372 * thread's stack frames. This calls excPrintStackFrame
373 * for each stack frame found.
374 *
375 *@@added V0.9.4 (2000-06-15) [umoeller]
376 */
377
378VOID excDumpStackFrames(FILE *file, // in: logfile from fopen()
379 PTIB ptib,
380 PCONTEXTRECORD pContextRec) // in: excpt info
381{
382 PULONG pulStackWord = 0;
383
384 fprintf(file, "\n\nStack frames:\n Address Module seg:ofs\n");
385
386 // first the trapping address itself
387 excPrintStackFrame(file,
388 "CS:EIP ",
389 pContextRec->ctx_RegEip);
390
391
392 pulStackWord = (PULONG)pContextRec->ctx_RegEbp;
393 /* if (pContextRec->ctx_RegEbp < pContextRec->ctx_RegEsp)
394 pulStackWord = (PULONG)(pContextRec->ctx_RegEbp & 0xFFFFFFF0);
395 else
396 pulStackWord = (PULONG)(pContextRec->ctx_RegEsp & 0xFFFFFFF0); */
397
398 while ( (pulStackWord != 0)
399 && (pulStackWord < (PULONG)ptib->tib_pstacklimit)
400 )
401 {
402 CHAR szAddress[20];
403
404 if (((ULONG)pulStackWord & 0x00000FFF) == 0x00000000)
405 {
406 // we're on a page boundary: check access
407 ULONG ulCountPages = 0x1000;
408 ULONG ulFlagsPage = 0;
409 APIRET arc = DosQueryMem((void *)pulStackWord,
410 &ulCountPages,
411 &ulFlagsPage);
412 if ( (arc != NO_ERROR)
413 || ( (arc == NO_ERROR)
414 && ( !( ((ulFlagsPage & (PAG_COMMIT|PAG_READ))
415 == (PAG_COMMIT|PAG_READ)
416 )
417 )
418 )
419 )
420 )
421 {
422 fprintf(file, "\n %08lX: ", (ULONG)pulStackWord);
423 fprintf(file, "Page inaccessible");
424 pulStackWord += 0x1000;
425 continue; // for
426 }
427 }
428
429 sprintf(szAddress, "%08lX",
430 (ULONG)pulStackWord);
431 excPrintStackFrame(file,
432 szAddress,
433 *(pulStackWord+1));
434 pulStackWord = (PULONG)*(pulStackWord);
435
436 if (pulStackWord == 0)
437 fprintf(file, "\n pulStackWord == 0");
438 else if (pulStackWord >= (PULONG)ptib->tib_pstacklimit)
439 fprintf(file, "\n pulStackWord >= (PULONG)ptib->tib_pstacklimit");
440 } // end while
441}
442
443/*
444 *@@ excExplainException:
445 * used by the exception handlers below to write
446 * LOTS of information about the exception into a logfile.
447 *
448 * This calls excPrintStackFrame for each stack frame.
449 *
450 *@@changed XWP V1.0.9 (2010-04-16) [pr]: add variable initialisation
451 */
452
453VOID excExplainException2(FILE *file, // in: logfile from fopen()
454 PSZ pszHandlerName, // in: descriptive string
455 PEXCEPTIONREPORTRECORD pReportRec, // in: excpt info
456 PCONTEXTRECORD pContextRec, // in: excpt info
457 PEXCEPTIONREGISTRATIONRECORD2 pRegRec2) // in: reg info
458{
459 ULONG aulBuf[3];
460 const char *pcszVersion = "unknown";
461
462 PTIB ptib = NULL;
463 PPIB ppib = NULL;
464 HMODULE hMod1 = NULLHANDLE, hMod2 = NULLHANDLE;
465 CHAR szMod1[CCHMAXPATH] = "unknown",
466 szMod2[CCHMAXPATH] = "unknown";
467 ULONG ulObjNum = -1,
468 ulOffset = -1;
469 ULONG ul;
470
471 ULONG ulOldPriority = 0x0100; // regular, delta 0
472
473 // raise global flag for whether this func is running
474 // V0.9.13 (2001-06-19) [umoeller]
475 G_ulExplainExceptionRunning++;
476
477 // raise this thread's priority, because this
478 // might take some time
479 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
480 if (ptib)
481 if (ptib->tib_ptib2)
482 {
483 ulOldPriority = ptib->tib_ptib2->tib2_ulpri;
484 DosSetPriority(PRTYS_THREAD,
485 PRTYC_REGULAR,
486 PRTYD_MAXIMUM,
487 0); // current thread
488 }
489
490 // make some noise
491#ifndef __NOEXCEPTIONBEEPS__ // V0.9.19 (2002-04-17) [umoeller]
492 if (G_fBeepOnException)
493 {
494 DosBeep( 250, 30);
495 DosBeep( 500, 30);
496 DosBeep(1000, 30);
497 DosBeep(2000, 30);
498 DosBeep(4000, 30);
499 DosBeep(2000, 30);
500 DosBeep(1000, 30);
501 DosBeep( 500, 30);
502 DosBeep( 250, 30);
503 }
504#endif
505
506 // generic exception info
507 DosQuerySysInfo(QSV_VERSION_MAJOR, // 11
508 QSV_VERSION_REVISION, // 13 V1.0.0 (2002-08-28) [umoeller]
509 &aulBuf, sizeof(aulBuf));
510 // Warp 3 is reported as 20.30
511 // Warp 4 is reported as 20.40
512 // Aurora is reported as 20.45
513
514 if (aulBuf[0] == 20)
515 {
516 switch (aulBuf[1])
517 {
518 case 30: pcszVersion = "Warp 3"; break;
519 case 40: pcszVersion = "Warp 4"; break;
520 case 45: pcszVersion = "WSeB kernel"; break;
521 }
522 }
523 fprintf(file,
524 "Running OS/2 version: %u.%u.%u (%s)\n",
525 aulBuf[0], // major
526 aulBuf[1],
527 aulBuf[2], // revision V1.0.0 (2002-08-28) [umoeller]
528 pcszVersion);
529
530
531 // generic exception info
532 fprintf(file,
533 "\n%s:\n Exception type: %08lX\n Address: %08lX\n Params: ",
534 pszHandlerName,
535 pReportRec->ExceptionNum,
536 (ULONG)pReportRec->ExceptionAddress);
537 for (ul = 0; ul < pReportRec->cParameters; ul++)
538 {
539 fprintf(file, "%08lX ",
540 pReportRec->ExceptionInfo[ul]);
541 }
542
543 // now explain the exception in a bit more detail;
544 // depending on the exception, pReportRec->ExceptionInfo
545 // contains some useful data
546 switch (pReportRec->ExceptionNum)
547 {
548 case XCPT_ACCESS_VIOLATION:
549 fprintf(file, "\nXCPT_ACCESS_VIOLATION: ");
550 if (pReportRec->ExceptionInfo[0] & XCPT_READ_ACCESS)
551 fprintf(file, "XCPT_READ_ACCESS -- Invalid read access from 0x%04lX:%08lX.\n",
552 pContextRec->ctx_SegDs,
553 pReportRec->ExceptionInfo[1]);
554 else if (pReportRec->ExceptionInfo[0] & XCPT_WRITE_ACCESS)
555 fprintf(file, "XCPT_WRITE_ACCESS -- Invalid write access to 0x%04lX:%08lX.\n",
556 pContextRec->ctx_SegDs,
557 pReportRec->ExceptionInfo[1]);
558 else if (pReportRec->ExceptionInfo[0] & XCPT_SPACE_ACCESS)
559 fprintf(file, "XCPT_SPACE_ACCESS -- Invalid space access at 0x%04lX.\n",
560 pReportRec->ExceptionInfo[1]);
561 else if (pReportRec->ExceptionInfo[0] & XCPT_LIMIT_ACCESS)
562 fprintf(file, "XCPT_LIMIT_ACCESS -- Invalid limit access occurred.\n");
563 else if (pReportRec->ExceptionInfo[0] == XCPT_UNKNOWN_ACCESS)
564 fprintf(file, "XCPT_UNKNOWN_ACCESS -- unknown at 0x%04lX:%08lX\n",
565 pContextRec->ctx_SegDs,
566 pReportRec->ExceptionInfo[1]);
567 fprintf(file,
568 "Explanation: An attempt was made to access a memory object which does\n"
569 " not belong to the current process. Most probable causes\n"
570 " for this are that an invalid pointer was used, there was\n"
571 " confusion with administering memory or error conditions \n"
572 " were not properly checked for.\n");
573 break;
574
575 case XCPT_INTEGER_DIVIDE_BY_ZERO:
576 fprintf(file, "\nXCPT_INTEGER_DIVIDE_BY_ZERO.\n");
577 fprintf(file,
578 "Explanation: An attempt was made to divide an integer value by zero,\n"
579 " which is not defined.\n");
580 break;
581
582 case XCPT_ILLEGAL_INSTRUCTION:
583 fprintf(file, "\nXCPT_ILLEGAL_INSTRUCTION.\n");
584 fprintf(file,
585 "Explanation: An attempt was made to execute an instruction that\n"
586 " is not defined on this machine's architecture.\n");
587 break;
588
589 case XCPT_PRIVILEGED_INSTRUCTION:
590 fprintf(file, "\nXCPT_PRIVILEGED_INSTRUCTION.\n");
591 fprintf(file,
592 "Explanation: An attempt was made to execute an instruction that\n"
593 " is not permitted in the current machine mode or that\n"
594 " the program had no permission to execute.\n");
595 break;
596
597 case XCPT_INTEGER_OVERFLOW:
598 fprintf(file, "\nXCPT_INTEGER_OVERFLOW.\n");
599 fprintf(file,
600 "Explanation: An integer operation generated a carry-out of the most\n"
601 " significant bit. This is a sign of an attempt to store\n"
602 " a value which does not fit into an integer variable.\n");
603 break;
604
605 default:
606 fprintf(file, "\nUnknown OS/2 exception number %d.\n", pReportRec->ExceptionNum);
607 fprintf(file, "Look this up in the OS/2 header files.\n");
608 break;
609 }
610
611 // V0.9.16 (2001-11-02) [pr]: We already got this info. above - this overwrites the
612 // original values before the priority change, which is rather confusing.
613 // if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
614 {
615 /*
616 * process info:
617 *
618 */
619
620 if ((ptib) && (ppib)) // (99-11-01) [umoeller]
621 {
622 if (pContextRec->ContextFlags & CONTEXT_CONTROL)
623 {
624 // get the main module
625 hMod1 = ppib->pib_hmte;
626 DosQueryModuleName(hMod1,
627 sizeof(szMod1),
628 szMod1);
629
630 // get the trapping module
631 DosQueryModFromEIP(&hMod2,
632 &ulObjNum,
633 sizeof(szMod2),
634 szMod2,
635 &ulOffset,
636 pContextRec->ctx_RegEip);
637 DosQueryModuleName(hMod2,
638 sizeof(szMod2),
639 szMod2);
640 }
641
642 fprintf(file,
643 "\nProcess information:"
644 "\n Process ID: 0x%lX"
645 "\n Process module: 0x%lX (%s)"
646 "\n Trapping module: 0x%lX (%s)"
647 "\n Object: %ld\n", // V0.9.16 (2001-11-02) [pr]: make this display signed
648 ppib->pib_ulpid,
649 hMod1, szMod1,
650 hMod2, szMod2,
651 ulObjNum);
652
653 fprintf(file,
654 "\nTrapping thread information:"
655 "\n Thread ID: 0x%lX (%lu)"
656 "\n Thread slot ID: 0x%lX (%lu)" // added V0.9.19 (2002-03-28) [umoeller]
657 "\n Priority: 0x%lX\n",
658 ptib->tib_ptib2->tib2_ultid, ptib->tib_ptib2->tib2_ultid,
659 ptib->tib_ordinal, ptib->tib_ordinal,
660 ulOldPriority);
661 }
662 else
663 fprintf(file, "\nProcess information was not available.");
664
665 // avoid trouble if the developer is using an older version of except.h
666#ifdef TRY_V2
667 if (pRegRec2 &&
668 pRegRec2->pvUser == &(pRegRec2->pszSetBy) &&
669 pRegRec2->pszSetBy)
670 {
671 fprintf(file,
672 "\nException handler:"
673 "\n Set by: %s\n",
674 pRegRec2->pszSetBy ? pRegRec2->pszSetBy : "unknown");
675
676 if (pRegRec2->pszFmt)
677 {
678 fprintf(file, " Details: ");
679 fprintf(file, pRegRec2->pszFmt,
680 pRegRec2->ulArg1, pRegRec2->ulArg2,
681 pRegRec2->ulArg3, pRegRec2->ulArg4);
682 }
683 }
684#endif
685
686 fprintf(file, "\n");
687
688 /*
689 * now call the hook, if one has been defined,
690 * so that the application can write additional
691 * information to the traplog (V0.9.0)
692 */
693
694 if (G_pfnExcHook)
695 G_pfnExcHook(file, ptib, ulOldPriority); // V0.9.16 (2001-12-02) [pr]
696
697 // *** registers
698
699 fprintf(file, "\nRegisters:");
700 if (pContextRec->ContextFlags & CONTEXT_INTEGER)
701 {
702 fprintf(file, "\n DS = %08lX ", pContextRec->ctx_SegDs);
703 fprintf(file, "\n ES = %08lX ", pContextRec->ctx_SegEs);
704 fprintf(file, "\n FS = %08lX ", pContextRec->ctx_SegFs);
705 fprintf(file, "\n GS = %08lX ", pContextRec->ctx_SegGs);
706
707 // EAX
708 fprintf(file, "\n EAX = %08lX ", pContextRec->ctx_RegEax);
709 excDescribePage(file, pContextRec->ctx_RegEax);
710 // EBX
711 fprintf(file, "\n EBX = %08lX ", pContextRec->ctx_RegEbx);
712 excDescribePage(file, pContextRec->ctx_RegEbx);
713 // ECX
714 fprintf(file, "\n ECX = %08lX ", pContextRec->ctx_RegEcx);
715 excDescribePage(file, pContextRec->ctx_RegEcx);
716 // EDX
717 fprintf(file, "\n EDX = %08lX ", pContextRec->ctx_RegEdx);
718 excDescribePage(file, pContextRec->ctx_RegEdx);
719 // ESI
720 fprintf(file, "\n ESI = %08lX ", pContextRec->ctx_RegEsi);
721 excDescribePage(file, pContextRec->ctx_RegEsi);
722 // EDI
723 fprintf(file, "\n EDI = %08lX ", pContextRec->ctx_RegEdi);
724 excDescribePage(file, pContextRec->ctx_RegEdi);
725 fprintf(file, "\n");
726 }
727 else
728 fprintf(file, " not available\n");
729
730 if (pContextRec->ContextFlags & CONTEXT_CONTROL)
731 {
732
733 // *** instruction
734
735 fprintf(file, "\nInstruction pointer (where exception occurred):\n CS:EIP = %04lX:%08lX ",
736 pContextRec->ctx_SegCs,
737 pContextRec->ctx_RegEip);
738 excDescribePage(file, pContextRec->ctx_RegEip);
739
740 // *** CPU flags
741
742 fprintf(file, "\n EFLAGS = %08lX\n", pContextRec->ctx_EFlags);
743
744 /*
745 * stack:
746 *
747 */
748
749 fprintf(file, "\nStack:\n Base: %08lX\n Limit: %08lX",
750 (ULONG)(ptib ? ptib->tib_pstack : 0),
751 (ULONG)(ptib ? ptib->tib_pstacklimit : 0));
752 fprintf(file, "\n SS:ESP = %04lX:%08lX ",
753 pContextRec->ctx_SegSs,
754 pContextRec->ctx_RegEsp);
755 excDescribePage(file, pContextRec->ctx_RegEsp);
756
757 fprintf(file, "\n EBP = %08lX ", pContextRec->ctx_RegEbp);
758 excDescribePage(file, pContextRec->ctx_RegEbp);
759
760 /*
761 * stack dump:
762 */
763
764 if (ptib != 0)
765 {
766 excDumpStackFrames(file, ptib, pContextRec);
767 }
768 }
769 }
770 fprintf(file, "\n");
771
772 // reset old priority
773 DosSetPriority(PRTYS_THREAD,
774 (ulOldPriority & 0x0F00) >> 8,
775 (UCHAR)ulOldPriority,
776 0); // current thread
777
778 // lower global flag again V0.9.13 (2001-06-19) [umoeller]
779 G_ulExplainExceptionRunning--;
780}
781
782VOID excExplainException(FILE *file, // in: logfile from fopen()
783 PSZ pszHandlerName, // in: descriptive string
784 PEXCEPTIONREPORTRECORD pReportRec, // in: excpt info
785 PCONTEXTRECORD pContextRec) // in: excpt info
786{
787 excExplainException2(file, pszHandlerName, pReportRec, pContextRec, 0);
788}
789
790/* ******************************************************************
791 *
792 * Exported routines
793 *
794 ********************************************************************/
795
796/*
797 *@@ excRegisterHooks:
798 * this registers hooks which get called for
799 * exception handlers. You can set any of the
800 * hooks to NULL for safe defaults (see top of
801 * except.c for details). You can set none,
802 * one, or both of the hooks, and you can call
803 * this function several times.
804 *
805 * Both hooks get called whenever an exception
806 * occurs, so there better be no bugs in these
807 * routines. ;-) They only get called from
808 * within excHandlerLoud (because excHandlerQuiet
809 * writes no trap logs).
810 *
811 * Registering hooks affects all threads that use
812 * the exception handlers.
813 *
814 * The hooks are as follows:
815 *
816 * -- pfnExcOpenFileNew gets called to open
817 * the trap log file. This can return a FILE*
818 * pointer from fopen() (which will be closed
819 * automatically by the handler).
820 *
821 * If this hook is not defined, ?:\TRAP.LOG is
822 * used. Use this hook to specify a different file
823 * and have some notes written into it before the
824 * actual exception info.
825 *
826 * If the hook returns a null FILE* pointer,
827 * logging is disabled, and the "loud" handler
828 * effectively behaves like the "quiet" handler.
829 *
830 * -- pfnExcHookNew gets called while the trap log
831 * is being written. At this point,
832 * the following info has been written into
833 * the trap log already:
834 *
835 * -- exception type/address block
836 *
837 * -- exception explanation
838 *
839 * -- process information
840 *
841 * _After_ the hook, the exception handler
842 * continues with the "Registers" information
843 * and stack dump/analysis.
844 *
845 * Use this hook to write additional application
846 * info into the trap log, such as the state
847 * of your own threads and mutexes.
848 *
849 * -- pfnExcHookError gets called when the TRY_* macros
850 * fail to install an exception handler (when
851 * DosSetExceptionHandler fails). I've never seen
852 * this happen.
853 *
854 *@@added V0.9.0 [umoeller]
855 *@@changed V0.9.2 (2000-03-10) [umoeller]: pfnExcHookError added
856 */
857
858VOID excRegisterHooks(PFNEXCOPENFILE pfnExcOpenFileNew,
859 PFNEXCHOOK pfnExcHookNew,
860 PFNEXCHOOKERROR pfnExcHookError,
861 BOOL fBeepOnExceptionNew)
862{
863 // adjust the global variables
864 G_pfnExcOpenFile = pfnExcOpenFileNew;
865 G_pfnExcHook = pfnExcHookNew;
866 G_pfnExcHookError = pfnExcHookError;
867 G_fBeepOnException = fBeepOnExceptionNew;
868}
869
870/*
871 *@@ excHandlerLoud:
872 * this is the "sophisticated" exception handler;
873 * which gives forth a loud sequence of beeps thru the
874 * speaker, writes a trap log and then returns back
875 * to the thread to continue execution, i.e. the
876 * default OS/2 exception handler will never get
877 * called.
878 *
879 * This requires a setjmp() call on
880 * EXCEPTIONREGISTRATIONRECORD2.jmpThread before
881 * being installed. The TRY_LOUD macro will take
882 * care of this for you (see except.c).
883 *
884 * This intercepts the following exceptions (see
885 * the OS/2 Control Program Reference for details):
886 *
887 * -- XCPT_ACCESS_VIOLATION (traps 0x0d, 0x0e)
888 * -- XCPT_INTEGER_DIVIDE_BY_ZERO (trap 0)
889 * -- XCPT_ILLEGAL_INSTRUCTION (trap 6)
890 * -- XCPT_PRIVILEGED_INSTRUCTION
891 * -- XCPT_INTEGER_OVERFLOW (trap 4)
892 *
893 * For these exceptions, we call the functions in debug.c
894 * to try to find debug code or SYM file information about
895 * what source code corresponds to the error.
896 *
897 * See excRegisterHooks for the default setup of this.
898 *
899 * Note that to get meaningful debugging information
900 * in this handler's traplog, you need the following:
901 *
902 * a) have a MAP file created at link time (/MAP)
903 *
904 * b) convert the MAP to a SYM file using MAPSYM
905 *
906 * c) put the SYM file in the same directory of
907 * the module (EXE or DLL). This must have the
908 * same filestem as the module.
909 *
910 * All other exceptions are passed to the next handler
911 * in the exception handler chain. This might be the
912 * C/C++ compiler handler or the default OS/2 handler,
913 * which will probably terminate the process.
914 *
915 *@@changed V0.9.0 [umoeller]: added support for thread termination
916 *@@changed V0.9.2 (2000-03-10) [umoeller]: switched date format to ISO
917 *@@changed V0.9.19 (2002-05-07) [umoeller]: added EXCEPTIONREPORTRECORD info so that catch block can check that
918 */
919
920ULONG _System excHandlerLoud(PEXCEPTIONREPORTRECORD pReportRec,
921 PEXCEPTIONREGISTRATIONRECORD2 pRegRec2,
922 PCONTEXTRECORD pContextRec,
923 PVOID pv)
924{
925 /* From the VAC++3 docs:
926 * "The first thing an exception handler should do is check the
927 * exception flags. If EH_EXIT_UNWIND is set, meaning
928 * the thread is ending, the handler tells the operating system
929 * to pass the exception to the next exception handler. It does the
930 * same if the EH_UNWINDING flag is set, the flag that indicates
931 * this exception handler is being removed.
932 * The EH_NESTED_CALL flag indicates whether the exception
933 * occurred within an exception handler. If the handler does
934 * not check this flag, recursive exceptions could occur until
935 * there is no stack remaining."
936 *
937 * So for all these conditions, we exit immediately.
938 */
939
940 // XWP V1.0.4 (2005-10-09) [pr]: Optimize
941 if (pReportRec->fHandlerFlags & (EH_EXIT_UNWIND | EH_UNWINDING | EH_NESTED_CALL))
942 return XCPT_CONTINUE_SEARCH;
943
944 switch (pReportRec->ExceptionNum)
945 {
946 case XCPT_ACCESS_VIOLATION:
947 case XCPT_INTEGER_DIVIDE_BY_ZERO:
948 case XCPT_ILLEGAL_INSTRUCTION:
949 case XCPT_PRIVILEGED_INSTRUCTION:
950 case XCPT_INVALID_LOCK_SEQUENCE:
951 case XCPT_INTEGER_OVERFLOW:
952 {
953 // "real" exceptions:
954 FILE *file = NULL;
955
956 // open traplog file;
957 if (G_pfnExcOpenFile)
958 // hook defined for this: call it
959 file = G_pfnExcOpenFile();
960 else
961 {
962 CHAR szFileName[100];
963 // no hook defined: open some
964 // default traplog file in root directory of
965 // boot drive
966 sprintf(szFileName, "%c:\\trap.log", doshQueryBootDrive());
967
968 if (file = fopen(szFileName, "a"))
969 {
970 DATETIME DT;
971 DosGetDateTime(&DT);
972 fprintf(file,
973 "\nTrap message -- Date: %04d-%02d-%02d, Time: %02d:%02d:%02d\n",
974 DT.year, DT.month, DT.day,
975 DT.hours, DT.minutes, DT.seconds);
976 fprintf(file, "------------------------------------------------\n");
977
978 }
979 }
980
981 if (file)
982 {
983 // write error log
984 excExplainException2(file,
985 "excHandlerLoud",
986 pReportRec,
987 pContextRec,
988 pRegRec2);
989 fclose(file);
990 }
991
992 // copy report rec to user buffer
993 // V0.9.19 (2002-05-07) [umoeller]
994 memcpy(&pRegRec2->err,
995 pReportRec,
996 sizeof(EXCEPTIONREPORTRECORD));
997
998 // jump back to failing routine
999 longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum);
1000 }
1001 break;
1002 }
1003
1004 // not handled
1005 return XCPT_CONTINUE_SEARCH;
1006}
1007
1008/*
1009 *@@ excHandlerQuiet:
1010 * "quiet" xcpt handler, which simply suppresses exceptions;
1011 * this is useful for certain error-prone functions, where
1012 * exceptions are likely to appear, for example used by
1013 * wpshCheckObject to implement a fail-safe SOM object check.
1014 *
1015 * This does _not_ write an error log and makes _no_ sound.
1016 * This simply jumps back to the trapping thread or
1017 * calls EXCEPTIONREGISTRATIONRECORD2.pfnOnKill.
1018 *
1019 * Other than that, this behaves like excHandlerLoud.
1020 *
1021 * This is best registered thru the TRY_QUIET macro
1022 * (new with V0.84, described in except.c), which
1023 * does the necessary setup.
1024 *
1025 *@@changed V0.9.0 [umoeller]: added support for thread termination
1026 *@@changed V0.9.19 (2002-05-07) [umoeller]: added EXCEPTIONREPORTRECORD info so that catch block can check that
1027 */
1028
1029ULONG _System excHandlerQuiet(PEXCEPTIONREPORTRECORD pReportRec,
1030 PEXCEPTIONREGISTRATIONRECORD2 pRegRec2,
1031 PCONTEXTRECORD pContextRec,
1032 PVOID pv)
1033{
1034 // XWP V1.0.4 (2005-10-09) [pr]: Optimize
1035 if (pReportRec->fHandlerFlags & (EH_EXIT_UNWIND | EH_UNWINDING | EH_NESTED_CALL))
1036 return XCPT_CONTINUE_SEARCH;
1037
1038 switch (pReportRec->ExceptionNum)
1039 {
1040 case XCPT_ACCESS_VIOLATION:
1041 case XCPT_INTEGER_DIVIDE_BY_ZERO:
1042 case XCPT_ILLEGAL_INSTRUCTION:
1043 case XCPT_PRIVILEGED_INSTRUCTION:
1044 case XCPT_INVALID_LOCK_SEQUENCE:
1045 case XCPT_INTEGER_OVERFLOW:
1046 // write excpt explanation only if the
1047 // resp. debugging #define is set (setup.h)
1048 #ifdef DEBUG_WRITEQUIETEXCPT
1049 {
1050 FILE *file = excOpenTraplogFile();
1051 excExplainException(file,
1052 "excHandlerQuiet",
1053 pReportRec,
1054 pContextRec
1055 pRegRec2);
1056 fclose(file);
1057 }
1058 #endif
1059
1060 // copy report rec to user buffer
1061 // V0.9.19 (2002-05-07) [umoeller]
1062 memcpy(&pRegRec2->err,
1063 pReportRec,
1064 sizeof(EXCEPTIONREPORTRECORD));
1065
1066 // jump back to failing routine
1067 longjmp(pRegRec2->jmpThread, pReportRec->ExceptionNum);
1068 break;
1069 }
1070
1071 return XCPT_CONTINUE_SEARCH;
1072}
1073
1074
Note: See TracBrowser for help on using the repository browser.