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

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

Common: System Exceptions:

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