source: trunk/src/tools/qsysxcpt_pm.cpp@ 147

Last change on this file since 147 was 147, checked in by dmik, 19 years ago

Common: System Exceptions:

  • Fixed the stack walking procedure so that the stack word beyond (above) the last valid (outermost) stack frame is not treated as EIP (therefore, it is not considered as a really last stack frame and isn't written) because it can actually contain any unrelated garbage.
  • Fixed an exception in the exception handler when DosQuerySysState() data was not available.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 36.7 KB
Line 
1/****************************************************************************
2** $Id: qsysxcpt_pm.cpp 147 2006-10-29 20:22:35Z dmik $
3**
4** OS/2 System Exception handling routines
5**
6** Copyright (C) 2006 netlabs.org.
7**
8** This file is part of the kernel module of the Qt GUI Toolkit.
9**
10** This file may be distributed under the terms of the Q Public License
11** as defined by Trolltech AS of Norway and appearing in the file
12** LICENSE.QPL included in the packaging of this file.
13**
14** This file may be distributed and/or modified under the terms of the
15** GNU General Public License version 2 as published by the Free Software
16** Foundation and appearing in the file LICENSE.GPL included in the
17** packaging of this file.
18**
19** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
20** licenses may use this file in accordance with the Qt Commercial License
21** Agreement provided with the Software.
22**
23** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
24** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
25**
26** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
27** information about Qt Commercial License Agreements.
28** See http://www.trolltech.com/qpl/ for QPL licensing information.
29** See http://www.trolltech.com/gpl/ for GPL licensing information.
30**
31** Contact info@trolltech.com if any conditions of this licensing are
32** not clear to you.
33**
34**********************************************************************/
35
36/*
37 * The below code was started as a partial cut & paste from the except.h
38 * and except.c sources from the xwphelpers package (which is a part of the
39 * xworkplace product, see www.xworkplace.org, xworkplace.netlabs.org).
40 * XWorkplace is Copyright (C) 1999-2002 Ulrich Moeller. It has changed a lot
41 * since then, but thanks to XWP authors for a good example.
42 */
43
44#if !defined(QT_OS2_NO_SYSEXCEPTIONS)
45
46#include "qt_os2.h"
47
48#include <stdio.h>
49#include <stdlib.h>
50#include <stdarg.h>
51#include <unistd.h>
52#include <string.h>
53#include <time.h>
54#include <errno.h>
55#include <sys/stat.h>
56
57/// @todo (r=dmik) add locking to:
58// a) handle simultaneous independent exceptions from different threads
59// b) raise (user) exceptions on all other threads in order to write their
60// contexts to the trap file when a real exception happens on one thread
61
62/*! \class QtOS2SysXcptMainHandler qt_os2.h
63
64 @todo (r=dmik) describe...
65
66 - must be instantiated on the stack of the main thread only
67 - should be instantiated before a QAplication instance is created
68 (does nothing otherwise)
69 - note that callback can be called on any thread
70*/
71
72/* static */
73bool QtOS2SysXcptMainHandler::installed = FALSE;
74QtOS2SysXcptCallback QtOS2SysXcptMainHandler::callback = NULL;
75ERR QtOS2SysXcptMainHandler::libcHandler = NULL;
76
77/*!
78 @todo (r=dmik) describe...
79*/
80QtOS2SysXcptMainHandler::QtOS2SysXcptMainHandler( QtOS2SysXcptCallback cb )
81{
82 rec.prev_structure = NULL;
83 rec.ExceptionHandler = NULL;
84
85 PTIB ptib = NULL;
86 DosGetInfoBlocks( &ptib, NULL );
87 Q_ASSERT( ptib && ptib->tib_ptib2 );
88
89 if ( ptib && ptib->tib_ptib2 )
90 {
91 // must be instantiated only on the main (first) thread
92 Q_ASSERT( ptib->tib_ptib2->tib2_ultid == 1 );
93 if ( ptib->tib_ptib2->tib2_ultid == 1 )
94 {
95 // must not be already instantiated
96 Q_ASSERT( installed == FALSE );
97 Q_ASSERT( libcHandler == NULL );
98 if ( installed == FALSE && libcHandler == NULL )
99 {
100 installed = TRUE;
101 callback = cb;
102 // install the exception handler for the main thread
103 rec.ExceptionHandler = handler;
104 DosSetExceptionHandler( &rec );
105 }
106 }
107 }
108}
109
110/*!
111 @todo (r=dmik) describe...
112*/
113QtOS2SysXcptMainHandler::~QtOS2SysXcptMainHandler()
114{
115 Q_ASSERT( rec.ExceptionHandler == handler || rec.ExceptionHandler == NULL );
116 if ( rec.ExceptionHandler == handler )
117 {
118 // uninstall the exception handler for the main thread
119 DosUnsetExceptionHandler( &rec );
120 rec.ExceptionHandler = NULL;
121 callback = NULL;
122 installed = FALSE;
123 }
124}
125
126static void qt_excEscapeString( FILE *file, const char *pcszStr );
127
128#define XcptPvt QtOS2SysXcptMainHandler::Private
129
130class XcptPvt
131{
132public:
133 static void callbackWriter( const char *msg );
134
135 static inline int askCallback( QtOS2SysXcptReq req )
136 {
137 if ( callback )
138 return callback( req, NULL, 0 );
139 return FALSE;
140 }
141
142 static inline int letCallback( QtOS2SysXcptReq req )
143 {
144 if ( callback )
145 return callback( req, callbackWriter, 0 );
146 return FALSE;
147 }
148
149 static FILE *file;
150};
151
152/* static */
153FILE *XcptPvt::file = NULL;
154
155/* static */
156void XcptPvt::callbackWriter( const char *msg )
157{
158 if ( file )
159 qt_excEscapeString( file, msg );
160}
161
162/*! \internal
163 Writes the given string with [<>&'"] characters escaped for XML.
164*/
165static void qt_excEscapeString( FILE *file, const char *pcszStr )
166{
167 const char *pcszChars = "<>&'\"";
168 const char *aszEntities[] = { "&lt;", "&gt;", "&amp;", "&apos;", "&quot;" };
169
170 const char *pcsz = pcszStr;
171 const char *pcszRepl = NULL;
172 size_t cbLen = 0;
173
174 if ( !pcsz )
175 return;
176
177 while( *pcsz )
178 {
179 cbLen = strcspn( pcsz, pcszChars );
180 fwrite( pcsz, 1, cbLen, file );
181 pcsz += cbLen;
182 if ( !*pcsz )
183 break;
184 cbLen = strchr( pcszChars, *pcsz ) - pcszChars;
185 pcszRepl = aszEntities[cbLen];
186 fwrite( pcszRepl, 1, strlen(pcszRepl), file );
187 ++ pcsz;
188 }
189}
190
191/*! \internal
192 Writes the given error message.
193*/
194static void qt_excWriteErrorMsg( FILE *file, ULONG ulIndent, const char *pcszMsg,
195 ... )
196{
197 char szBuf[1024];
198 va_list args;
199 va_start( args, pcszMsg );
200 fprintf( file, "%*s<Error message=\"", ulIndent, "" );
201 vsnprintf( szBuf, sizeof(szBuf), pcszMsg, args );
202 szBuf[sizeof(szBuf) - 1] = '\0';
203 qt_excEscapeString( file, szBuf );
204 fprintf( file, "\"/>\n" );
205 va_end( args );
206}
207
208/*! \internal
209 Writes the register name, value and optionally memory flags
210*/
211static void qt_excWriteReg( FILE *file, const char *pszName, ULONG ulValue,
212 BOOL bQueryMem = TRUE )
213{
214 fprintf( file, " <Register name=\"%s\" value=\"%08lX\"",
215 pszName, ulValue );
216
217 if ( bQueryMem )
218 {
219 APIRET arc;
220 ULONG ulCount = 4;
221 ULONG ulFlags = 0;
222 arc = DosQueryMem( (PVOID) ulValue, &ulCount, &ulFlags );
223
224 if ( arc == NO_ERROR || arc == ERROR_INVALID_ADDRESS )
225 {
226 if ( arc == NO_ERROR )
227 {
228 fprintf( file, " flags=\"%08lX\"", ulFlags );
229 if ( ulFlags & (PAG_COMMIT | PAG_READ) == (PAG_COMMIT | PAG_READ) )
230 fprintf( file, " word=\"%08lX\"/>\n", *(ULONG *) ulValue );
231 else
232 fprintf( file, "/>\n" );
233 }
234 else
235 fprintf( file, " flags=\"invalid\"/>\n" );
236 }
237 else
238 {
239 fprintf( file, ">\n" );
240 qt_excWriteErrorMsg( file, 6, "DosQueryMem returned %lu"
241 "and flags %08lX", arc, ulFlags );
242 fprintf( file, " </Register>\n" );
243 }
244 }
245 else
246 fprintf( file, "/>\n" );
247}
248
249/*! \internal
250 Writes information about a signle stack frame.
251*/
252static void qt_excWriteStackFrame (FILE *file, ULONG ulRegEbp, ULONG ulRegEip)
253{
254 APIRET arc = NO_ERROR;
255 HMODULE hMod = NULLHANDLE;
256 char szMod[CCHMAXPATH] = "";
257 ULONG ulObject = 0,
258 ulOffset = 0;
259
260 fprintf (file, " <Frame pointer=\"%08lX\">\n", ulRegEbp);
261 fprintf (file, " <Location address=\"%08lX\">\n", ulRegEip);
262
263 arc = DosQueryModFromEIP (&hMod, &ulObject,
264 sizeof (szMod), szMod, &ulOffset,
265 ulRegEip);
266
267 if (arc != NO_ERROR)
268 qt_excWriteErrorMsg (file, 7, "%s: DosQueryModFromEIP returned %lu",
269 szMod, arc);
270 else
271 fprintf (file, " <Module ID=\"%04lX\" segment=\"%04lX\" "
272 "offset=\"%08lX\"/>\n",
273 hMod, ulObject + 1, ulOffset);
274
275 /* write a small memory dump with the location address in the middle */
276 {
277 enum { enmDelta = 8 };
278 UCHAR *pch = (UCHAR *) ulRegEip - enmDelta;
279 UCHAR *pchEnd = (UCHAR *) ulRegEip + enmDelta - 1;
280 ULONG ulCount = enmDelta * 2;
281 ULONG ulFlags = 0;
282 APIRET arc = NO_ERROR;
283 while (1)
284 {
285 arc = DosQueryMem ((void *) (ULONG) pch, &ulCount, &ulFlags);
286 if (arc == NO_ERROR)
287 {
288 if (ulCount >= enmDelta * 2)
289 break;
290 if (pch + ulCount <= (UCHAR *) ulRegEip)
291 {
292 /* ulAddress is outside the pch object */
293 pch += ulCount;
294 ulCount = enmDelta * 2 - ulCount;
295 }
296 else
297 {
298 /* ulAddress is within the pch object */
299 pchEnd = pch += ulCount;
300 break;
301 }
302 }
303 else if (arc == ERROR_INVALID_ADDRESS)
304 {
305 if (((ULONG) pch) & 0xFFFFF000 == ulRegEip & 0xFFFFF000)
306 break; /* the same page, ulAddress inaccessible */
307 pch = (UCHAR *) (ulRegEip & 0xFFFFF000);
308 }
309 }
310 fprintf (file, " <Dump address=\"%08lX\">\n ", pch);
311 if (arc == NO_ERROR &&
312 ulFlags & (PAG_COMMIT|PAG_READ) == (PAG_COMMIT | PAG_READ))
313 {
314 for (; pch < pchEnd; ++pch)
315 fprintf (file, "%02lX%c", (ULONG) *pch,
316 ulRegEip - (ULONG) pch == 1 ? '-' : ' ');
317 fprintf (file, "\n");
318 }
319 else
320 qt_excWriteErrorMsg (file, 0, "%08lX: DosQueryMem returned %lu"
321 "and flags %08lX", pch, arc, ulFlags);
322 fprintf (file, " </Dump>\n");
323 }
324
325 fprintf (file, " </Location>\n");
326 fprintf (file, " </Frame>\n");
327}
328
329/*! \internal
330 Walks the stack and writes information about stack frames.
331*/
332static void qt_excWriteStackFrames (FILE *file, PTIB ptib,
333 PCONTEXTRECORD pContextRec)
334{
335 ULONG ulRegEbp = pContextRec->ctx_RegEbp;
336 ULONG ulRegEip = pContextRec->ctx_RegEip;
337
338 fprintf (file, " <Frames>\n");
339
340 /* first the trapping address itself */
341 qt_excWriteStackFrame (file, ulRegEbp, ulRegEip);
342
343 /* first call to qt_excWriteStackFrame() is done before the EBP validity
344 * check below to get a chance to call qt_excWriteStackFrame() for the
345 * trapping address itself even if EBP there is invalid. */
346
347 while (ulRegEbp != 0 && ulRegEbp < (ULONG) ptib->tib_pstacklimit)
348 {
349 /* skip the trapping stack frame -- already written above */
350 if (pContextRec->ctx_RegEbp != ulRegEbp)
351 qt_excWriteStackFrame (file, ulRegEbp, ulRegEip);
352
353 if ((ulRegEbp & 0x00000FFF) == 0x00000000)
354 {
355 /* we're on a page boundary: check access */
356 ULONG ulCount = 0x1000;
357 ULONG ulFlags = 0;
358 APIRET arc = DosQueryMem ((void *) ulRegEbp, &ulCount, &ulFlags);
359 if ( (arc != NO_ERROR)
360 || ( arc == NO_ERROR
361 && (ulFlags & (PAG_COMMIT|PAG_READ))
362 != (PAG_COMMIT|PAG_READ)))
363 {
364 fprintf (file, " <Frame pointer=\"%08lX\">\n", ulRegEbp);
365 qt_excWriteErrorMsg (file, 6, "DosQueryMem returned %lu "
366 "and flags %08lX", arc, ulFlags);
367 fprintf (file, " </Frame>\n");
368 /* try go to the next page */
369 /// @todo (r=dmik) I don't know how much it is accurate,
370 // I've just taken the logic from xwphelpers sources.
371 ulRegEbp += 0x1000;
372 continue; /* while */
373 }
374 }
375
376 /* get the return address of the current call */
377 ulRegEip = *(((PULONG) ulRegEbp) + 1);
378 /* get the address of the outer stack frame */
379 ulRegEbp = *((PULONG) ulRegEbp);
380 }
381
382 fprintf (file, " </Frames>\n");
383}
384
385/*! \internal
386 Writes the thread information.
387 If ptib is not NULL, the current thread's information is to be written.
388 If ptib is NULL, pThrdRec is guaranted not to be NULL.
389*/
390static void qt_excWriteThreadInfo (FILE *file, PTIB ptib, QSTREC *pThrdRec,
391 PCONTEXTRECORD pContextRec)
392{
393 if (ptib)
394 {
395 fprintf (file, " <Thread ID=\"%04lX\" slot=\"%04lX\" "
396 "priority=\"%04lX\" ",
397 ptib->tib_ptib2->tib2_ultid, ptib->tib_ordinal,
398 ptib->tib_ptib2->tib2_ulpri);
399 if (pThrdRec)
400 fprintf (file, "state=\"%02lX\" ", (ULONG) pThrdRec->state);
401 fprintf (file, "mc=\"%04lX\" mcf=\"%04lX\">\n",
402 ptib->tib_ptib2->tib2_usMCCount,
403 ptib->tib_ptib2->tib2_fMCForceFlag);
404 }
405 else
406 {
407 fprintf (file, " <Thread ID=\"%04lX\" slot=\"%04lX\" "
408 "priority=\"%04lX\" state=\"%02lX\">\n",
409 pThrdRec->tid, pThrdRec->slot, pThrdRec->priority,
410 pThrdRec->state);
411 }
412
413 /* *** registers */
414
415 fprintf (file, " <CPU>\n"
416 " <Registers>\n");
417
418 if (pContextRec->ContextFlags & CONTEXT_SEGMENTS)
419 {
420 qt_excWriteReg (file, "DS", pContextRec->ctx_SegDs, FALSE);
421 qt_excWriteReg (file, "ES", pContextRec->ctx_SegEs, FALSE);
422 qt_excWriteReg (file, "FS", pContextRec->ctx_SegFs, FALSE);
423 qt_excWriteReg (file, "GS", pContextRec->ctx_SegGs, FALSE);
424 }
425
426 if (pContextRec->ContextFlags & CONTEXT_INTEGER)
427 {
428 qt_excWriteReg (file, "EAX", pContextRec->ctx_RegEax);
429 qt_excWriteReg (file, "EBX", pContextRec->ctx_RegEbx);
430 qt_excWriteReg (file, "ECX", pContextRec->ctx_RegEcx);
431 qt_excWriteReg (file, "EDX", pContextRec->ctx_RegEdx);
432 qt_excWriteReg (file, "ESI", pContextRec->ctx_RegEsi);
433 qt_excWriteReg (file, "EDI", pContextRec->ctx_RegEdi);
434 }
435
436 if (pContextRec->ContextFlags & CONTEXT_CONTROL)
437 {
438 qt_excWriteReg (file, "CS", pContextRec->ctx_SegCs, FALSE);
439 qt_excWriteReg (file, "EIP", pContextRec->ctx_RegEip);
440 qt_excWriteReg (file, "SS", pContextRec->ctx_SegSs, FALSE);
441 qt_excWriteReg (file, "ESP", pContextRec->ctx_RegEsp);
442 qt_excWriteReg (file, "EBP", pContextRec->ctx_RegEbp);
443 qt_excWriteReg (file, "EFLAGS", pContextRec->ctx_EFlags, FALSE);
444 }
445
446 fprintf (file, " </Registers>\n"
447 " </CPU>\n");
448
449 /* *** stack */
450
451 fprintf (file, " <Stack base=\"%08lX\" limit=\"%08lX\">\n",
452 (ULONG) ptib->tib_pstack,
453 (ULONG) ptib->tib_pstacklimit);
454
455 if (pContextRec->ContextFlags & CONTEXT_CONTROL)
456 {
457 qt_excWriteStackFrames (file, ptib, pContextRec);
458 }
459
460 fprintf (file, " </Stack>\n"
461 " </Thread>\n");
462}
463
464typedef struct _PROCESSINFO
465{
466 /* common values */
467 char *pszFullName;
468 char *pszBaseName;
469 /* direct info pointers */
470 PPIB ppib;
471 PTIB ptib;
472 BOOL bHaveSysState; /**< TRUE when both pProcRec and pLibRec are not NULL */
473 QSPREC *pProcRec; /**< NULL when bHaveSysState is FALSE */
474 QSLREC *pLibRec; /**< NULL when bHaveSysState is FALSE */
475} PROCESSINFO;
476
477/*! \internal
478 Writes module information to the log file.
479 \a ulOrigin is 1 (self), 2 (imported), or 3 (loaded). Other values are
480 ignored.
481*/
482static void qt_excWriteModule (FILE *file, USHORT hmte, char *pszName,
483 ULONG ulOrigin = 0)
484{
485 static const char *apszOrigins[] = { "self", "imported", "loaded" };
486
487 fprintf (file, " <Module ID=%\"%04lX\" name=\"", hmte);
488 qt_excEscapeString (file, pszName);
489 if (ulOrigin >= 1 && ulOrigin <= 3)
490 fprintf (file, "\"\n"
491 " origin=\"%s\"/>\n",
492 apszOrigins [ulOrigin - 1]);
493 else
494 fprintf (file, "\"/>\n");
495}
496
497/*! \internal
498 Writes module and all its imports information to the log file.
499 \a ulOrigin is 1 (self), or 3 (loaded), other values are ignored.
500*/
501static void qt_excWriteModules (FILE *file, USHORT hmte, QSLREC *pFirstLibRec,
502 ULONG ulOrigin)
503{
504 QSLREC *pLibRec = pFirstLibRec;
505 while (pLibRec)
506 {
507 if (pLibRec->hmte == hmte &&
508 pLibRec->pName /* not yet visited */)
509 {
510 qt_excWriteModule (file, hmte, (char *) pLibRec->pName, ulOrigin);
511 /* mark as visited */
512 pLibRec->pName = NULL;
513 /* go through imports */
514 if (pLibRec->ctImpMod)
515 {
516 USHORT *pHmte = (USHORT *) (pLibRec + 1);
517 for (ULONG i = 0; i < pLibRec->ctImpMod; ++ i, ++ pHmte)
518 qt_excWriteModules (file, *pHmte, pFirstLibRec,
519 ulOrigin == 1 ? 2 : ulOrigin);
520 break;
521 }
522 }
523 pLibRec = (QSLREC *) pLibRec->pNextRec;
524 }
525}
526
527/** Struct for recursive qt_excWriteModulesOnStack() to reduce stack usage */
528typedef struct _WMOS_STATE
529{
530 FILE *file;
531 ULONG ulRegEbp;
532 ULONG ulRegEip;
533 ULONG ulStackLimit;
534 USHORT hmteExe;
535 ULONG cLevel;
536 USHORT *pHmteOuter;
537} WMOS_STATE;
538
539/*! \internal
540 Walks the stack and writes information about all encountered modules.
541 Used only when DosQuerySysState() fails (so qt_excWriteModules() is not
542 applicable). We use recursiveness to avoid module record duplicates.
543*/
544static void qt_excWriteModulesOnStack (WMOS_STATE *pState)
545{
546 USHORT hmteThis = 0;
547
548 {
549 APIRET arc = NO_ERROR;
550 char szMod [CCHMAXPATH] = "";
551 ULONG ulObject = 0, ulOffset = 0;
552
553 arc = DosQueryModFromEIP ((HMODULE *) &hmteThis, &ulObject,
554 sizeof (szMod), szMod, &ulOffset,
555 pState->ulRegEip);
556 if (arc == NO_ERROR && hmteThis != pState->hmteExe)
557 {
558 /* look if we've already seen this module using the hmteThis chain
559 * on the stack */
560 USHORT *pH = &hmteThis;
561 /* calculate distance between recursive hmteThis on the stack */
562 ULONG ulOuter = (ULONG) pState->pHmteOuter - (ULONG) pH;
563 ULONG cL = pState->cLevel;
564 while (cL != 0)
565 {
566 pH = (USHORT *)(((ULONG) pH) + ulOuter);
567 if (*pH == hmteThis)
568 break;
569 -- cL;
570 }
571 if (cL == 0)
572 {
573 /* got an unique module handle */
574 DosQueryModuleName (hmteThis, sizeof (szMod), szMod);
575 qt_excWriteModule (pState->file, hmteThis, szMod);
576 }
577 }
578 }
579
580 /* we do EBP validity check here as well as before the recursive
581 * call below to get a chance for the above code (EIP to hmod translation)
582 * to be executed even if EBP there is invalid. */
583
584 if (pState->ulRegEbp != 0 && pState->ulRegEbp < pState->ulStackLimit)
585 {
586 if ((pState->ulRegEbp & 0x00000FFF) == 0x00000000)
587 {
588 /* we're on a page boundary: check access */
589 while (pState->ulRegEbp < pState->ulStackLimit)
590 {
591 ULONG ulCount = 0x1000;
592 ULONG ulFlags = 0;
593 APIRET arc = DosQueryMem ((void *) pState->ulRegEbp,
594 &ulCount, &ulFlags);
595 if ( (arc != NO_ERROR)
596 || ( arc == NO_ERROR
597 && (ulFlags & (PAG_COMMIT|PAG_READ))
598 != (PAG_COMMIT|PAG_READ)))
599 {
600 /* try go to the next page */
601 /// @todo (r=dmik) I don't know how much it is accurate,
602 // I've just taken the logic from xwphelpers sources.
603 pState->ulRegEbp += 0x1000;
604 continue; /* while */
605 }
606 break;
607 }
608 }
609
610 /* get the return address of the current call */
611 pState->ulRegEip = *(((PULONG) pState->ulRegEbp) + 1);
612 /* get the address of the outer stack frame */
613 pState->ulRegEbp = *((PULONG) pState->ulRegEbp);
614
615 if (pState->ulRegEbp != 0 && pState->ulRegEbp < pState->ulStackLimit)
616 {
617 pState->cLevel ++;
618 pState->pHmteOuter = &hmteThis;
619 qt_excWriteModulesOnStack (pState);
620 }
621 }
622}
623
624/*! \internal
625 Writes exception information to the log file.
626*/
627static void qt_excWriteException (FILE *file,
628 PROCESSINFO *pInfo,
629 PEXCEPTIONREPORTRECORD pReportRec,
630 PCONTEXTRECORD pContextRec)
631{
632 ULONG ul = 0;
633 ULONG aulBuf [3];
634
635 /* *** application info */
636
637 {
638 fprintf (file, " <Application name=\"");
639 if (XcptPvt::askCallback (QtOS2SysXcptReq_AppName) == TRUE)
640 XcptPvt::letCallback (QtOS2SysXcptReq_AppName);
641 else
642 qt_excEscapeString (file, pInfo->pszBaseName);
643 fprintf (file, "\" version=\"");
644 if (XcptPvt::askCallback (QtOS2SysXcptReq_AppVer) == TRUE)
645 XcptPvt::letCallback (QtOS2SysXcptReq_AppVer);
646 else
647 fprintf (file, "unknown");
648 fprintf (file, "\">\n");
649
650 if (XcptPvt::askCallback (QtOS2SysXcptReq_ReportTo) == TRUE)
651 {
652 fprintf (file, " <Report to=\"");
653 XcptPvt::letCallback (QtOS2SysXcptReq_ReportTo);
654 if (XcptPvt::askCallback (QtOS2SysXcptReq_ReportSubj) == TRUE)
655 {
656 fprintf (file, "\" subject=\"");
657 XcptPvt::letCallback (QtOS2SysXcptReq_ReportSubj);
658 }
659 fprintf (file, "\"/>\n");
660 }
661
662 fprintf (file, " </Application>\n");
663 }
664
665 /* *** generic exception info */
666
667 fprintf (file,
668 " <Exception type=\"%08lX\" flags=\"%08lX\" address=\"%08lX\">\n",
669 pReportRec->ExceptionNum, pReportRec->fHandlerFlags,
670 (ULONG) pReportRec->ExceptionAddress);
671
672 for (ul = 0; ul < pReportRec->cParameters; ++ul)
673 {
674 fprintf (file, " <Param value=\"%08lX\"/>\n",
675 pReportRec->ExceptionInfo [ul]);
676 }
677
678 fprintf (file, " </Exception>\n");
679
680 /* *** system info */
681
682 DosQuerySysInfo (QSV_VERSION_MAJOR, QSV_VERSION_REVISION,
683 &aulBuf, sizeof (aulBuf));
684 /* Warp 3 is reported as 20.30 */
685 /* Warp 4 is reported as 20.40 */
686 /* Aurora is reported as 20.45 */
687
688 fprintf (file,
689 " <System name=\"OS/2\" version=\"%u.%u.%u\"/>\n",
690 aulBuf[0], aulBuf[1], aulBuf[2]);
691
692 /* *** process info */
693
694 fprintf (file, " <Process ID=\"%04lX\" parentID=\"%04lX\">\n",
695 pInfo->ppib->pib_ulpid, pInfo->ppib->pib_ulppid);
696
697 fprintf (file, " <Modules>\n");
698 if (pInfo->bHaveSysState)
699 {
700 /* write process module and it's imports */
701 qt_excWriteModules (file, pInfo->ppib->pib_hmte, pInfo->pLibRec, 1);
702 /* write loaded modules and their imports */
703 if (pInfo->pProcRec->cLib && pInfo->pProcRec->pLibRec)
704 {
705 for (ULONG i = 0; i < pInfo->pProcRec->cLib; ++ i)
706 qt_excWriteModules (file, pInfo->pProcRec->pLibRec [i],
707 pInfo->pLibRec, 3);
708 }
709 }
710 else
711 {
712 qt_excWriteModule (file, pInfo->ppib->pib_hmte, pInfo->pszFullName, 1);
713 WMOS_STATE State = { file, pContextRec->ctx_RegEbp,
714 pContextRec->ctx_RegEip,
715 (ULONG) pInfo->ptib->tib_pstacklimit,
716 pInfo->ppib->pib_hmte,
717 0 /* cLevel */, NULL /* pHmteOuter */ };
718 qt_excWriteModulesOnStack (&State);
719 }
720 fprintf (file, " </Modules>\n");
721
722 fprintf (file, " <Threads>\n");
723
724 /* first, the current thread */
725 QSTREC *pThrdRec = pInfo->bHaveSysState ? pInfo->pProcRec->pThrdRec : NULL;
726 if (pThrdRec)
727 {
728 /* locate the current thread structure */
729 for (ul = 0; ul < pInfo->pProcRec->cTCB; ++ ul, ++ pThrdRec)
730 if ((ULONG) pThrdRec->tid == pInfo->ptib->tib_ptib2->tib2_ultid)
731 break;
732 if (ul == pInfo->pProcRec->cTCB)
733 pThrdRec = NULL;
734 }
735 qt_excWriteThreadInfo (file, pInfo->ptib, pThrdRec, pContextRec);
736
737 fprintf (file, " </Threads>\n");
738
739 fprintf (file, " </Process>\n");
740}
741
742/**
743 * Opens an unique log file using \a pcszBasePath as the base path.
744 * On input, \a pszFileName is the desired base file name w/o extension.
745 * If it doesn't fit into the file name length limit (ENAMETOOLONG) for the
746 * FS of the given disk, then a 8x3 file name is attemptet as a fallback.
747 * On output, \a pszFileName will contain the full file name of the opened
748 * log file. Note that \a pszFileName must be at least CCHMAXPATH bytes length.
749 */
750static FILE *qt_excOpenLogFile( const char *pcszBasePath, char *pszFileName )
751{
752 FILE *file = NULL;
753
754 char szFileName[CCHMAXPATH];
755 char szDir[] = "\\.qt3traps";
756 char szFile[] = "\\12345678.123";
757 enum { cbFileName = sizeof(szFileName) };
758 enum { cbDir = sizeof(szDir) };
759 enum { cbFile = sizeof(szFile) };
760
761 ULONG ul = 0;
762 ULONG cbDirLen = 0;
763 ULONG cbLen = 0;
764 ULONG ulStamp = 0;
765 char *pszStamp = NULL;
766
767 if ( pcszBasePath == NULL || pszFileName == NULL )
768 return NULL;
769
770 if ( access( pcszBasePath, F_OK ) != 0 )
771 return NULL;
772
773 if ( strlen( pcszBasePath ) + cbDir + cbFile - 2 >= CCHMAXPATH )
774 return NULL;
775
776 // get the full path if it's not 'X:'
777 if ( strlen(pcszBasePath) != 2 ||
778 pcszBasePath[1] != ':' )
779 {
780 if( DosQueryPathInfo( pcszBasePath, FIL_QUERYFULLNAME,
781 szFileName, cbFileName ) != NO_ERROR )
782 return NULL;
783 }
784 else
785 strcpy( szFileName, pcszBasePath );
786
787 strcat( szFileName, szDir );
788 if ( access( szFileName, F_OK ) != 0 )
789 if ( mkdir( szFileName, 0777 ) != 0 )
790 return NULL;
791
792 cbDirLen = strlen( szFileName );
793
794 // first, try to use the desired base file name
795
796 // we will append -NN to the file name to add some 'fraction of a
797 // second' granularity to avoid name conflicts (0 <= NNN <= 99)
798 cbLen = strlen( pszFileName );
799 if ( cbDirLen + cbLen + 8 /* \-NN.xml */ < CCHMAXPATH )
800 {
801 strcat( szFileName, "\\" );
802 strcat( szFileName, pszFileName );
803 cbLen += cbDirLen + 1 /* \ */;
804 for( ul = 0; ul < 100; ++ul )
805 {
806 sprintf( szFileName + cbLen, "-%02ld.xml", ul );
807 if ( access( szFileName, F_OK ) == 0 )
808 continue;
809 file = fopen( szFileName, "wt" );
810 if ( file )
811 break;
812 if ( errno == ENAMETOOLONG )
813 break;
814 }
815 }
816
817 // next, try a time stamp as a 8x3 file name
818 if ( file == NULL )
819 {
820 pszStamp = szFileName + cbDirLen + 1;
821 strcat( szFileName, szFile );
822
823 ulStamp = time( NULL );
824
825 // In order to add some 'fraction of a second' granularity to the
826 // timestamp, we shift it by 5 bits. This gives us 0x07FFFFFF seconds
827 // which is a period of approx. 4,25 years. 5 bits in turn give us 32
828 // fractions of a second.
829 ulStamp <<= 5;
830
831 // try some few adajcent stamps if the first one fails
832 for ( ul = 0; ul < 32; ++ul, ++ulStamp )
833 {
834 sprintf( pszStamp, "%08lX.xml", ulStamp );
835 if ( access( szFileName, F_OK ) == 0 )
836 continue;
837 file = fopen( szFileName, "wt" );
838 if ( file )
839 break;
840 }
841 }
842
843 if ( file )
844 {
845 strncpy( pszFileName, szFileName, CCHMAXPATH - 1 );
846 pszFileName[CCHMAXPATH - 1] = '\0';
847 }
848
849 return file;
850}
851
852/*! \internal
853 Qt Exception handler.
854*/
855/* static */
856ULONG APIENTRY
857QtOS2SysXcptMainHandler::handler( PEXCEPTIONREPORTRECORD pReportRec,
858 PEXCEPTIONREGISTRATIONRECORD pRegRec,
859 PCONTEXTRECORD pContextRec,
860 PVOID pv )
861{
862 PROCESSINFO Info = {0};
863
864 /* From the VAC++3 docs:
865 * "The first thing an exception handler should do is check the
866 * exception flags. If EH_EXIT_UNWIND is set, meaning
867 * the thread is ending, the handler tells the operating system
868 * to pass the exception to the next exception handler. It does the
869 * same if the EH_UNWINDING flag is set, the flag that indicates
870 * this exception handler is being removed.
871 * The EH_NESTED_CALL flag indicates whether the exception
872 * occurred within an exception handler. If the handler does
873 * not check this flag, recursive exceptions could occur until
874 * there is no stack remaining."
875 *
876 * So for all these conditions, we exit immediately.
877 */
878
879 if (pReportRec->fHandlerFlags &
880 (EH_EXIT_UNWIND | EH_UNWINDING | EH_NESTED_CALL))
881 return XCPT_CONTINUE_SEARCH;
882
883 /* setup the process info structure */
884 DosGetInfoBlocks (&Info.ptib, &Info.ppib);
885
886 switch (pReportRec->ExceptionNum)
887 {
888 case XCPT_ACCESS_VIOLATION:
889 case XCPT_INTEGER_DIVIDE_BY_ZERO:
890 case XCPT_ILLEGAL_INSTRUCTION:
891 case XCPT_PRIVILEGED_INSTRUCTION:
892 case XCPT_INVALID_LOCK_SEQUENCE:
893 case XCPT_INTEGER_OVERFLOW:
894 {
895 /* "real" exceptions: */
896
897 APIRET arc = NO_ERROR;
898 PVOID pBuf = NULL;
899
900 char szFullExeName [CCHMAXPATH] = "";
901 char szBaseExeName [CCHMAXPATH] = "";
902
903 TIB2 Tib2Orig;
904 DATETIME dt;
905
906 char szFileName [CCHMAXPATH];
907 FILE *file = NULL;
908
909 /* raise this thread's priority to do it fast */
910 Tib2Orig = *Info.ptib->tib_ptib2;
911 Info.ptib->tib_ptib2 = &Tib2Orig;
912 DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0);
913
914 DosBeep (880, 200);
915
916 /* query system state */
917 {
918 ULONG ulCB = 64 * 1024;
919 ULONG cTries = 3;
920 while (-- cTries)
921 {
922 arc = DosAllocMem (&pBuf, ulCB, PAG_WRITE | PAG_COMMIT);
923 if (arc == NO_ERROR)
924 {
925 memset (pBuf, 0, ulCB);
926 arc = DosQuerySysState (QS_PROCESS | QS_MTE, 0,
927 Info.ppib->pib_ulpid, 0,
928 pBuf, ulCB);
929 if (arc == NO_ERROR)
930 {
931 QSPTRREC *pPtrRec = (QSPTRREC *) pBuf;
932 Info.bHaveSysState = TRUE;
933 Info.pProcRec = pPtrRec->pProcRec;
934 Info.pLibRec = pPtrRec->pLibRec;
935 }
936 else if (arc == ERROR_BUFFER_OVERFLOW)
937 {
938 /* retry with some bigger buffer size */
939 DosFreeMem (pBuf);
940 pBuf = NULL;
941 ulCB *= 2;
942 continue;
943 }
944 }
945 break;
946 }
947 }
948
949 /* get the main module name */
950 DosQueryModuleName (Info.ppib->pib_hmte,
951 sizeof (szFullExeName), szFullExeName);
952 {
953 /* find the base name (w/o path and extension) */
954 char *psz = strrchr (szFullExeName, '\\');
955 if ( psz )
956 ++ psz;
957 if ( !psz )
958 psz = szFullExeName;
959 strcpy (szBaseExeName, psz);
960 psz = strrchr (szBaseExeName, '.');
961 if (psz)
962 *psz = '\0';
963 Info.pszFullName = szFullExeName;
964 Info.pszBaseName = szBaseExeName;
965 }
966
967 /* compose the desired base file name for the log file
968 * (<datetime>-<exe>) */
969 DosGetDateTime (&dt);
970 sprintf (szFileName, "%04d%02d%02d%02d%02d%02d-%s",
971 dt.year, dt.month, dt.day,
972 dt.hours, dt.minutes, dt.seconds,
973 szBaseExeName);
974
975 /* open the log file: */
976
977 /* first, try the home directory, then the root drive
978 * (see QDir::homeDirPath()) */
979 file = qt_excOpenLogFile (getenv ("HOME"), szFileName);
980 if (file == NULL)
981 {
982 char *pszHomeDrive = getenv ("HOMEDRIVE");
983 char *pszHomePath = getenv ("HOMEPATH");
984 if (pszHomeDrive && pszHomePath &&
985 strlen (pszHomeDrive) + strlen (pszHomePath) < CCHMAXPATH)
986 {
987 char szHomePath [CCHMAXPATH];
988 strcpy (szHomePath, pszHomeDrive);
989 strcat (szHomePath, pszHomePath);
990 file = qt_excOpenLogFile (szHomePath, szFileName);
991 }
992 }
993 if (file == NULL)
994 {
995 char szBootDrive[] = "\0:";
996 ULONG ulBootDrive;
997 DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
998 &ulBootDrive, sizeof(ulBootDrive) );
999 szBootDrive[0] = (char) ulBootDrive + 'A' - 1;
1000 file = qt_excOpenLogFile (szBootDrive, szFileName);
1001 }
1002
1003 if (file != NULL)
1004 {
1005 fprintf (file,
1006 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
1007 "<!--\n"
1008 " A fatal error has occured while running this application.\n"
1009 " Please contact the application vendor, describe what happened\n"
1010 " and send the contents of this file saved locally as\n"
1011 " '%s'.\n"
1012 "-->\n",
1013 szFileName);
1014
1015 fprintf (file,
1016 "<Trap timestamp=\"%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d\" "
1017 "version=\"1.0\"\n"
1018 " generator=\"Qt Library Version %s\"\n"
1019 " xmlns=\"http://db.hugaida.com/xml/os2/sys/Trap\">\n",
1020 dt.year, dt.month, dt.day,
1021 dt.hours, dt.minutes, dt.seconds,
1022 (dt.timezone > 0 ? '-' : '+'),
1023 abs (dt.timezone / 60),
1024 abs (dt.timezone % 60),
1025 QT_VERSION_STR );
1026
1027 XcptPvt::file = file;
1028
1029 /* write trap information */
1030 qt_excWriteException (file, &Info, pReportRec, pContextRec);
1031
1032 XcptPvt::file = NULL;
1033
1034 fprintf (file, "</Trap>\n\n");
1035 fclose (file);
1036
1037 /* attempt to display the created file */
1038 {
1039 STARTDATA SData = {0};
1040 PID pid = 0;
1041 ULONG ulSessID = 0;
1042
1043 SData.Length = sizeof (STARTDATA);
1044 SData.PgmName = "E.EXE";
1045 SData.PgmInputs = szFileName;
1046
1047 DosStartSession (&SData, &ulSessID, &pid);
1048 }
1049 }
1050 else
1051 DosBeep (220, 200);
1052
1053 if (pBuf != NULL)
1054 DosFreeMem (pBuf);
1055
1056 /* reset old priority */
1057 DosSetPriority (PRTYS_THREAD, (Tib2Orig.tib2_ulpri & 0x0F00) >> 8,
1058 (UCHAR) Tib2Orig.tib2_ulpri,
1059 0);
1060 }
1061 break;
1062 }
1063
1064 /* we never handle the exception ourselves */
1065
1066 if (Info.ptib->tib_ptib2->tib2_ultid == 1 &&
1067 QtOS2SysXcptMainHandler::libcHandler != NULL)
1068 {
1069 /* we are on the main thread and were installed from qt_init() during
1070 * QApplication initialization using a hack; pass control back to the
1071 * LIBC exception handler */
1072 return QtOS2SysXcptMainHandler::libcHandler (pReportRec, pRegRec,
1073 pContextRec, pv);
1074 }
1075
1076 return XCPT_CONTINUE_SEARCH;
1077}
1078
1079#endif // !defined(QT_PM_NO_SYSEXCEPTIONS)
Note: See TracBrowser for help on using the repository browser.