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

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

Common: System Exceptions:

  • Added reading out and writing the module description as the 'desc' attribute of the 'Module' element;
  • Added writing the module last modification date as the 'timestamp' attribute of the 'Module' element.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 40.7 KB
Line 
1/****************************************************************************
2** $Id: qsysxcpt_pm.cpp 148 2006-11-01 00:12:16Z 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, 7, "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, 8, "%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, 7, "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
464#ifndef max
465#define max(a,b) (((a) > (b)) ? (a) : (b))
466#endif
467
468/** @internal
469 * Writes the module description string (if found) as the 'desciption'
470 * attribute. Returns TRUE if the description was found and written.
471 */
472static BOOL qt_excWriteModuleDesc (FILE *file, char *pszFileName)
473{
474 /* see OS2TK\h\exe.h, OS2TK\h\exe386.h and LXSPEC.INF for details */
475 enum { EXEID = 0x5a4d, LXOffset = 0x3C, sizeof_exe = LXOffset + 4,
476 E32MAGIC = 0x584c, e32_nrestab = 34 * 4,
477 sizeof_e32 = e32_nrestab + 4,
478 max_nresname = 255 + 1 /* trailing 0 */ };
479
480 UCHAR achBuf [max (max_nresname, max (sizeof_e32, sizeof_exe))];
481
482 BOOL bFound = FALSE;
483 ULONG ul1 = 0, ul2 = 0;
484
485 HFILE hf = NULL;
486 APIRET arc = NO_ERROR;
487
488 arc = DosOpen (pszFileName, &hf, &ul1,
489 0, 0, OPEN_ACTION_OPEN_IF_EXISTS,
490 OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY,
491 NULL);
492 if (arc != NO_ERROR)
493 return FALSE;
494
495 do
496 {
497 /* read the old EXE header plus the offset to the LX header */
498 arc = DosRead (hf, achBuf, sizeof_exe, &ul1);
499 if (arc != NO_ERROR || sizeof_exe != ul1)
500 break;
501
502 if (*(USHORT *) &achBuf == EXEID)
503 {
504 /* go to the LX header */
505 ul1 = *(ULONG *) &achBuf [LXOffset];
506 arc = DosSetFilePtr (hf, (LONG) ul1, FILE_BEGIN, &ul2);
507 if (arc != NO_ERROR || ul1 != ul2)
508 break;
509
510 /* read it upto the e32_nrestab field */
511 arc = DosRead (hf, achBuf, sizeof_e32, &ul1);
512 if (arc != NO_ERROR || sizeof_e32 != ul1 ||
513 *(USHORT *) &achBuf != E32MAGIC)
514 break;
515 }
516 else
517 if (*(USHORT *) &achBuf == E32MAGIC)
518 {
519 /* there may be no old EXE header, but LX only,
520 * read it upto the e32_nrestab field */
521 if (sizeof_e32 > sizeof_exe)
522 {
523 arc = DosRead (hf, achBuf + sizeof_exe,
524 sizeof_e32 - sizeof_exe, &ul1);
525 if (arc != NO_ERROR || sizeof_e32 - sizeof_exe != ul1)
526 break;
527 }
528 }
529 else
530 break;
531
532 /* go to the beginning of the non-resident name table */
533 ul1 = *(ULONG *) &achBuf [e32_nrestab];
534 if (ul1)
535 {
536 arc = DosSetFilePtr (hf, (LONG) ul1, FILE_BEGIN, &ul2);
537 if (arc != NO_ERROR || ul1 != ul2)
538 break;
539
540 /* read the first entry */
541 arc = DosRead (hf, achBuf, max_nresname, &ul1);
542 if (arc != NO_ERROR || max_nresname != ul1)
543 break;
544
545 /* if the ordinal is 0, then it's a description field (we don't do
546 * the full table search, since it shoud be ordered by ordinals) */
547 if (*(USHORT *) &achBuf [1 + *achBuf] == 0)
548 {
549 /* place the trailing zero */
550 achBuf [1 + *achBuf] = 0;
551 /* write the description attribute */
552 fprintf (file, "\n desc=\"%s\"", achBuf + 1);
553 bFound = TRUE;
554 }
555 }
556 }
557 while (0);
558
559 DosClose (hf);
560 return bFound;
561}
562
563/*! \internal
564 Writes module information to the log file.
565 \a ulOrigin is 1 (self), 2 (imported), or 3 (loaded). Other values are
566 ignored.
567*/
568static void qt_excWriteModule (FILE *file, USHORT hmte, char *pszName,
569 ULONG ulOrigin = 0)
570{
571 static const char *apszOrigins[] = { "self", "imported", "loaded" };
572
573 FILESTATUS3 Status;
574 APIRET arc = NO_ERROR;
575
576 fprintf (file, " <Module ID=%\"%04lX\" name=\"", hmte);
577 qt_excEscapeString (file, pszName);
578 fprintf (file, "\"");
579
580 arc = DosQueryPathInfo (pszName, FIL_STANDARD, &Status, sizeof (Status));
581
582 if ((ulOrigin >= 1 && ulOrigin <= 3) || arc == NO_ERROR )
583 {
584 fprintf (file, "\n ");
585 if (ulOrigin >= 1 && ulOrigin <= 3)
586 fprintf (file, " origin=\"%s\"",
587 apszOrigins [ulOrigin - 1]);
588 if (arc == NO_ERROR)
589 fprintf (file, " timestamp=\"%04d-%02d-%02dT%02d:%02d:%02d\"",
590 Status.fdateLastWrite.year + 1980,
591 Status.fdateLastWrite.month, Status.fdateLastWrite.day,
592 Status.ftimeLastWrite.hours, Status.ftimeLastWrite.minutes,
593 Status.ftimeLastWrite.twosecs * 2);
594 }
595
596 qt_excWriteModuleDesc (file, pszName);
597
598 fprintf (file, "/>\n");
599}
600
601/*! \internal
602 Writes module and all its imports information to the log file.
603 \a ulOrigin is 1 (self), or 3 (loaded), other values are ignored.
604*/
605static void qt_excWriteModules (FILE *file, USHORT hmte, QSLREC *pFirstLibRec,
606 ULONG ulOrigin)
607{
608 QSLREC *pLibRec = pFirstLibRec;
609 while (pLibRec)
610 {
611 if (pLibRec->hmte == hmte &&
612 pLibRec->pName /* not yet visited */)
613 {
614 qt_excWriteModule (file, hmte, (char *) pLibRec->pName, ulOrigin);
615 /* mark as visited */
616 pLibRec->pName = NULL;
617 /* go through imports */
618 if (pLibRec->ctImpMod)
619 {
620 USHORT *pHmte = (USHORT *) (pLibRec + 1);
621 for (ULONG i = 0; i < pLibRec->ctImpMod; ++ i, ++ pHmte)
622 qt_excWriteModules (file, *pHmte, pFirstLibRec,
623 ulOrigin == 1 ? 2 : ulOrigin);
624 break;
625 }
626 }
627 pLibRec = (QSLREC *) pLibRec->pNextRec;
628 }
629}
630
631/** @internal
632 * Struct for recursive qt_excWriteModulesOnStack() to reduce stack usage
633 */
634typedef struct _WMOS_STATE
635{
636 FILE *file;
637 ULONG ulRegEbp;
638 ULONG ulRegEip;
639 ULONG ulStackLimit;
640 USHORT hmteExe;
641 ULONG cLevel;
642 USHORT *pHmteOuter;
643} WMOS_STATE;
644
645/*! \internal
646 Walks the stack and writes information about all encountered modules.
647 Used only when DosQuerySysState() fails (so qt_excWriteModules() is not
648 applicable). We use recursiveness to avoid module record duplicates.
649*/
650static void qt_excWriteModulesOnStack (WMOS_STATE *pState)
651{
652 USHORT hmteThis = 0;
653
654 {
655 APIRET arc = NO_ERROR;
656 char szMod [CCHMAXPATH] = "";
657 ULONG ulObject = 0, ulOffset = 0;
658
659 arc = DosQueryModFromEIP ((HMODULE *) &hmteThis, &ulObject,
660 sizeof (szMod), szMod, &ulOffset,
661 pState->ulRegEip);
662 if (arc == NO_ERROR && hmteThis != pState->hmteExe)
663 {
664 /* look if we've already seen this module using the hmteThis chain
665 * on the stack */
666 USHORT *pH = &hmteThis;
667 /* calculate distance between recursive hmteThis on the stack */
668 ULONG ulOuter = (ULONG) pState->pHmteOuter - (ULONG) pH;
669 ULONG cL = pState->cLevel;
670 while (cL != 0)
671 {
672 pH = (USHORT *)(((ULONG) pH) + ulOuter);
673 if (*pH == hmteThis)
674 break;
675 -- cL;
676 }
677 if (cL == 0)
678 {
679 /* got an unique module handle */
680 DosQueryModuleName (hmteThis, sizeof (szMod), szMod);
681 qt_excWriteModule (pState->file, hmteThis, szMod);
682 }
683 }
684 }
685
686 /* we do EBP validity check here as well as before the recursive
687 * call below to get a chance for the above code (EIP to hmod translation)
688 * to be executed even if EBP there is invalid. */
689
690 if (pState->ulRegEbp != 0 && pState->ulRegEbp < pState->ulStackLimit)
691 {
692 if ((pState->ulRegEbp & 0x00000FFF) == 0x00000000)
693 {
694 /* we're on a page boundary: check access */
695 while (pState->ulRegEbp < pState->ulStackLimit)
696 {
697 ULONG ulCount = 0x1000;
698 ULONG ulFlags = 0;
699 APIRET arc = DosQueryMem ((void *) pState->ulRegEbp,
700 &ulCount, &ulFlags);
701 if ( (arc != NO_ERROR)
702 || ( arc == NO_ERROR
703 && (ulFlags & (PAG_COMMIT|PAG_READ))
704 != (PAG_COMMIT|PAG_READ)))
705 {
706 /* try go to the next page */
707 /// @todo (r=dmik) I don't know how much it is accurate,
708 // I've just taken the logic from xwphelpers sources.
709 pState->ulRegEbp += 0x1000;
710 continue; /* while */
711 }
712 break;
713 }
714 }
715
716 /* get the return address of the current call */
717 pState->ulRegEip = *(((PULONG) pState->ulRegEbp) + 1);
718 /* get the address of the outer stack frame */
719 pState->ulRegEbp = *((PULONG) pState->ulRegEbp);
720
721 if (pState->ulRegEbp != 0 && pState->ulRegEbp < pState->ulStackLimit)
722 {
723 pState->cLevel ++;
724 pState->pHmteOuter = &hmteThis;
725 qt_excWriteModulesOnStack (pState);
726 }
727 }
728}
729
730/** @internal Simple process info struct */
731typedef struct _PROCESSINFO
732{
733 /* common values */
734 char *pszFullName;
735 char *pszBaseName;
736 /* direct info pointers */
737 PPIB ppib;
738 PTIB ptib;
739 BOOL bHaveSysState; /**< TRUE when both pProcRec and pLibRec are not NULL */
740 QSPREC *pProcRec; /**< NULL when bHaveSysState is FALSE */
741 QSLREC *pLibRec; /**< NULL when bHaveSysState is FALSE */
742} PROCESSINFO;
743
744/*! \internal
745 Writes exception information to the log file.
746*/
747static void qt_excWriteException (FILE *file,
748 PROCESSINFO *pInfo,
749 PEXCEPTIONREPORTRECORD pReportRec,
750 PCONTEXTRECORD pContextRec)
751{
752 ULONG ul = 0;
753 ULONG aulBuf [3];
754
755 /* *** application info */
756
757 {
758 fprintf (file, " <Application name=\"");
759 if (XcptPvt::askCallback (QtOS2SysXcptReq_AppName) == TRUE)
760 XcptPvt::letCallback (QtOS2SysXcptReq_AppName);
761 else
762 qt_excEscapeString (file, pInfo->pszBaseName);
763 fprintf (file, "\" version=\"");
764 if (XcptPvt::askCallback (QtOS2SysXcptReq_AppVer) == TRUE)
765 XcptPvt::letCallback (QtOS2SysXcptReq_AppVer);
766 else
767 fprintf (file, "unknown");
768 fprintf (file, "\">\n");
769
770 if (XcptPvt::askCallback (QtOS2SysXcptReq_ReportTo) == TRUE)
771 {
772 fprintf (file, " <Report to=\"");
773 XcptPvt::letCallback (QtOS2SysXcptReq_ReportTo);
774 if (XcptPvt::askCallback (QtOS2SysXcptReq_ReportSubj) == TRUE)
775 {
776 fprintf (file, "\" subject=\"");
777 XcptPvt::letCallback (QtOS2SysXcptReq_ReportSubj);
778 }
779 fprintf (file, "\"/>\n");
780 }
781
782 fprintf (file, " </Application>\n");
783 }
784
785 /* *** generic exception info */
786
787 fprintf (file,
788 " <Exception type=\"%08lX\" flags=\"%08lX\" address=\"%08lX\">\n",
789 pReportRec->ExceptionNum, pReportRec->fHandlerFlags,
790 (ULONG) pReportRec->ExceptionAddress);
791
792 for (ul = 0; ul < pReportRec->cParameters; ++ul)
793 {
794 fprintf (file, " <Param value=\"%08lX\"/>\n",
795 pReportRec->ExceptionInfo [ul]);
796 }
797
798 fprintf (file, " </Exception>\n");
799
800 /* *** system info */
801
802 DosQuerySysInfo (QSV_VERSION_MAJOR, QSV_VERSION_REVISION,
803 &aulBuf, sizeof (aulBuf));
804 /* Warp 3 is reported as 20.30 */
805 /* Warp 4 is reported as 20.40 */
806 /* Aurora is reported as 20.45 */
807
808 fprintf (file,
809 " <System name=\"OS/2\" version=\"%u.%u.%u\"/>\n",
810 aulBuf[0], aulBuf[1], aulBuf[2]);
811
812 /* *** process info */
813
814 fprintf (file, " <Process ID=\"%04lX\" parentID=\"%04lX\">\n",
815 pInfo->ppib->pib_ulpid, pInfo->ppib->pib_ulppid);
816
817 fprintf (file, " <Modules>\n");
818 if (pInfo->bHaveSysState)
819 {
820 /* write process module and it's imports */
821 qt_excWriteModules (file, pInfo->ppib->pib_hmte, pInfo->pLibRec, 1);
822 /* write loaded modules and their imports */
823 if (pInfo->pProcRec->cLib && pInfo->pProcRec->pLibRec)
824 {
825 for (ULONG i = 0; i < pInfo->pProcRec->cLib; ++ i)
826 qt_excWriteModules (file, pInfo->pProcRec->pLibRec [i],
827 pInfo->pLibRec, 3);
828 }
829 }
830 else
831 {
832 qt_excWriteModule (file, pInfo->ppib->pib_hmte, pInfo->pszFullName, 1);
833 WMOS_STATE State = { file, pContextRec->ctx_RegEbp,
834 pContextRec->ctx_RegEip,
835 (ULONG) pInfo->ptib->tib_pstacklimit,
836 pInfo->ppib->pib_hmte,
837 0 /* cLevel */, NULL /* pHmteOuter */ };
838 qt_excWriteModulesOnStack (&State);
839 }
840 fprintf (file, " </Modules>\n");
841
842 fprintf (file, " <Threads>\n");
843
844 /* first, the current thread */
845 QSTREC *pThrdRec = pInfo->bHaveSysState ? pInfo->pProcRec->pThrdRec : NULL;
846 if (pThrdRec)
847 {
848 /* locate the current thread structure */
849 for (ul = 0; ul < pInfo->pProcRec->cTCB; ++ ul, ++ pThrdRec)
850 if ((ULONG) pThrdRec->tid == pInfo->ptib->tib_ptib2->tib2_ultid)
851 break;
852 if (ul == pInfo->pProcRec->cTCB)
853 pThrdRec = NULL;
854 }
855 qt_excWriteThreadInfo (file, pInfo->ptib, pThrdRec, pContextRec);
856
857 fprintf (file, " </Threads>\n");
858
859 fprintf (file, " </Process>\n");
860}
861
862/**
863 * Opens an unique log file using \a pcszBasePath as the base path.
864 * On input, \a pszFileName is the desired base file name w/o extension.
865 * If it doesn't fit into the file name length limit (ENAMETOOLONG) for the
866 * FS of the given disk, then a 8x3 file name is attemptet as a fallback.
867 * On output, \a pszFileName will contain the full file name of the opened
868 * log file. Note that \a pszFileName must be at least CCHMAXPATH bytes length.
869 */
870static FILE *qt_excOpenLogFile( const char *pcszBasePath, char *pszFileName )
871{
872 FILE *file = NULL;
873
874 char szFileName[CCHMAXPATH];
875 char szDir[] = "\\.qt3traps";
876 char szFile[] = "\\12345678.123";
877 enum { cbFileName = sizeof(szFileName) };
878 enum { cbDir = sizeof(szDir) };
879 enum { cbFile = sizeof(szFile) };
880
881 ULONG ul = 0;
882 ULONG cbDirLen = 0;
883 ULONG cbLen = 0;
884 ULONG ulStamp = 0;
885 char *pszStamp = NULL;
886
887 if ( pcszBasePath == NULL || pszFileName == NULL )
888 return NULL;
889
890 if ( access( pcszBasePath, F_OK ) != 0 )
891 return NULL;
892
893 if ( strlen( pcszBasePath ) + cbDir + cbFile - 2 >= CCHMAXPATH )
894 return NULL;
895
896 // get the full path if it's not 'X:'
897 if ( strlen(pcszBasePath) != 2 ||
898 pcszBasePath[1] != ':' )
899 {
900 if( DosQueryPathInfo( pcszBasePath, FIL_QUERYFULLNAME,
901 szFileName, cbFileName ) != NO_ERROR )
902 return NULL;
903 }
904 else
905 strcpy( szFileName, pcszBasePath );
906
907 strcat( szFileName, szDir );
908 if ( access( szFileName, F_OK ) != 0 )
909 if ( mkdir( szFileName, 0777 ) != 0 )
910 return NULL;
911
912 cbDirLen = strlen( szFileName );
913
914 // first, try to use the desired base file name
915
916 // we will append -NN to the file name to add some 'fraction of a
917 // second' granularity to avoid name conflicts (0 <= NNN <= 99)
918 cbLen = strlen( pszFileName );
919 if ( cbDirLen + cbLen + 8 /* \-NN.xml */ < CCHMAXPATH )
920 {
921 strcat( szFileName, "\\" );
922 strcat( szFileName, pszFileName );
923 cbLen += cbDirLen + 1 /* \ */;
924 for( ul = 0; ul < 100; ++ul )
925 {
926 sprintf( szFileName + cbLen, "-%02ld.xml", ul );
927 if ( access( szFileName, F_OK ) == 0 )
928 continue;
929 file = fopen( szFileName, "wt" );
930 if ( file )
931 break;
932 if ( errno == ENAMETOOLONG )
933 break;
934 }
935 }
936
937 // next, try a time stamp as a 8x3 file name
938 if ( file == NULL )
939 {
940 pszStamp = szFileName + cbDirLen + 1;
941 strcat( szFileName, szFile );
942
943 ulStamp = time( NULL );
944
945 // In order to add some 'fraction of a second' granularity to the
946 // timestamp, we shift it by 5 bits. This gives us 0x07FFFFFF seconds
947 // which is a period of approx. 4,25 years. 5 bits in turn give us 32
948 // fractions of a second.
949 ulStamp <<= 5;
950
951 // try some few adajcent stamps if the first one fails
952 for ( ul = 0; ul < 32; ++ul, ++ulStamp )
953 {
954 sprintf( pszStamp, "%08lX.xml", ulStamp );
955 if ( access( szFileName, F_OK ) == 0 )
956 continue;
957 file = fopen( szFileName, "wt" );
958 if ( file )
959 break;
960 }
961 }
962
963 if ( file )
964 {
965 strncpy( pszFileName, szFileName, CCHMAXPATH - 1 );
966 pszFileName[CCHMAXPATH - 1] = '\0';
967 }
968
969 return file;
970}
971
972/*! \internal
973 Qt Exception handler.
974*/
975/* static */
976ULONG APIENTRY
977QtOS2SysXcptMainHandler::handler( PEXCEPTIONREPORTRECORD pReportRec,
978 PEXCEPTIONREGISTRATIONRECORD pRegRec,
979 PCONTEXTRECORD pContextRec,
980 PVOID pv )
981{
982 PROCESSINFO Info = {0};
983
984 /* From the VAC++3 docs:
985 * "The first thing an exception handler should do is check the
986 * exception flags. If EH_EXIT_UNWIND is set, meaning
987 * the thread is ending, the handler tells the operating system
988 * to pass the exception to the next exception handler. It does the
989 * same if the EH_UNWINDING flag is set, the flag that indicates
990 * this exception handler is being removed.
991 * The EH_NESTED_CALL flag indicates whether the exception
992 * occurred within an exception handler. If the handler does
993 * not check this flag, recursive exceptions could occur until
994 * there is no stack remaining."
995 *
996 * So for all these conditions, we exit immediately.
997 */
998
999 if (pReportRec->fHandlerFlags &
1000 (EH_EXIT_UNWIND | EH_UNWINDING | EH_NESTED_CALL))
1001 return XCPT_CONTINUE_SEARCH;
1002
1003 /* setup the process info structure */
1004 DosGetInfoBlocks (&Info.ptib, &Info.ppib);
1005
1006 switch (pReportRec->ExceptionNum)
1007 {
1008 case XCPT_ACCESS_VIOLATION:
1009 case XCPT_INTEGER_DIVIDE_BY_ZERO:
1010 case XCPT_ILLEGAL_INSTRUCTION:
1011 case XCPT_PRIVILEGED_INSTRUCTION:
1012 case XCPT_INVALID_LOCK_SEQUENCE:
1013 case XCPT_INTEGER_OVERFLOW:
1014 {
1015 /* "real" exceptions: */
1016
1017 APIRET arc = NO_ERROR;
1018 PVOID pBuf = NULL;
1019
1020 char szFullExeName [CCHMAXPATH] = "";
1021 char szBaseExeName [CCHMAXPATH] = "";
1022
1023 TIB2 Tib2Orig;
1024 DATETIME dt;
1025
1026 char szFileName [CCHMAXPATH];
1027 FILE *file = NULL;
1028
1029 /* raise this thread's priority to do it fast */
1030 Tib2Orig = *Info.ptib->tib_ptib2;
1031 Info.ptib->tib_ptib2 = &Tib2Orig;
1032 DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0);
1033
1034 DosBeep (880, 200);
1035
1036 /* query system state */
1037 {
1038 ULONG ulCB = 64 * 1024;
1039 ULONG cTries = 3;
1040 while (-- cTries)
1041 {
1042 arc = DosAllocMem (&pBuf, ulCB, PAG_WRITE | PAG_COMMIT);
1043 if (arc == NO_ERROR)
1044 {
1045 memset (pBuf, 0, ulCB);
1046 arc = DosQuerySysState (QS_PROCESS | QS_MTE, 0,
1047 Info.ppib->pib_ulpid, 0,
1048 pBuf, ulCB);
1049 if (arc == NO_ERROR)
1050 {
1051 QSPTRREC *pPtrRec = (QSPTRREC *) pBuf;
1052 Info.bHaveSysState = TRUE;
1053 Info.pProcRec = pPtrRec->pProcRec;
1054 Info.pLibRec = pPtrRec->pLibRec;
1055 }
1056 else if (arc == ERROR_BUFFER_OVERFLOW)
1057 {
1058 /* retry with some bigger buffer size */
1059 DosFreeMem (pBuf);
1060 pBuf = NULL;
1061 ulCB *= 2;
1062 continue;
1063 }
1064 }
1065 break;
1066 }
1067 }
1068
1069 /* get the main module name */
1070 DosQueryModuleName (Info.ppib->pib_hmte,
1071 sizeof (szFullExeName), szFullExeName);
1072 {
1073 /* find the base name (w/o path and extension) */
1074 char *psz = strrchr (szFullExeName, '\\');
1075 if ( psz )
1076 ++ psz;
1077 if ( !psz )
1078 psz = szFullExeName;
1079 strcpy (szBaseExeName, psz);
1080 psz = strrchr (szBaseExeName, '.');
1081 if (psz)
1082 *psz = '\0';
1083 Info.pszFullName = szFullExeName;
1084 Info.pszBaseName = szBaseExeName;
1085 }
1086
1087 /* compose the desired base file name for the log file
1088 * (<datetime>-<exe>) */
1089 DosGetDateTime (&dt);
1090 sprintf (szFileName, "%04d%02d%02d%02d%02d%02d-%s",
1091 dt.year, dt.month, dt.day,
1092 dt.hours, dt.minutes, dt.seconds,
1093 szBaseExeName);
1094
1095 /* open the log file: */
1096
1097 /* first, try the home directory, then the root drive
1098 * (see QDir::homeDirPath()) */
1099 file = qt_excOpenLogFile (getenv ("HOME"), szFileName);
1100 if (file == NULL)
1101 {
1102 char *pszHomeDrive = getenv ("HOMEDRIVE");
1103 char *pszHomePath = getenv ("HOMEPATH");
1104 if (pszHomeDrive && pszHomePath &&
1105 strlen (pszHomeDrive) + strlen (pszHomePath) < CCHMAXPATH)
1106 {
1107 char szHomePath [CCHMAXPATH];
1108 strcpy (szHomePath, pszHomeDrive);
1109 strcat (szHomePath, pszHomePath);
1110 file = qt_excOpenLogFile (szHomePath, szFileName);
1111 }
1112 }
1113 if (file == NULL)
1114 {
1115 char szBootDrive[] = "\0:";
1116 ULONG ulBootDrive;
1117 DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
1118 &ulBootDrive, sizeof(ulBootDrive) );
1119 szBootDrive[0] = (char) ulBootDrive + 'A' - 1;
1120 file = qt_excOpenLogFile (szBootDrive, szFileName);
1121 }
1122
1123 if (file != NULL)
1124 {
1125 fprintf (file,
1126 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
1127 "<!--\n"
1128 " A fatal error has occured while running this application.\n"
1129 " Please contact the application vendor, describe what happened\n"
1130 " and send the contents of this file saved locally as\n"
1131 " '%s'.\n"
1132 "-->\n",
1133 szFileName);
1134
1135 fprintf (file,
1136 "<Trap timestamp=\"%04d-%02d-%02dT%02d:%02d:%02d\"" //%c%02d:%02d\""
1137 " version=\"1.0\"\n"
1138 " generator=\"Qt Library Version %s\"\n"
1139 " xmlns=\"http://db.hugaida.com/xml/os2/sys/Trap\">\n",
1140 dt.year, dt.month, dt.day,
1141 dt.hours, dt.minutes, dt.seconds,
1142 //(dt.timezone > 0 ? '-' : '+'),
1143 //abs (dt.timezone / 60),
1144 //abs (dt.timezone % 60),
1145 QT_VERSION_STR);
1146
1147 XcptPvt::file = file;
1148
1149 /* write trap information */
1150 qt_excWriteException (file, &Info, pReportRec, pContextRec);
1151
1152 XcptPvt::file = NULL;
1153
1154 fprintf (file, "</Trap>\n\n");
1155 fclose (file);
1156
1157 /* attempt to display the created file */
1158 {
1159 STARTDATA SData = {0};
1160 PID pid = 0;
1161 ULONG ulSessID = 0;
1162
1163 SData.Length = sizeof (STARTDATA);
1164 SData.PgmName = "E.EXE";
1165 SData.PgmInputs = szFileName;
1166
1167 DosStartSession (&SData, &ulSessID, &pid);
1168 }
1169 }
1170 else
1171 DosBeep (220, 200);
1172
1173 if (pBuf != NULL)
1174 DosFreeMem (pBuf);
1175
1176 /* reset old priority */
1177 DosSetPriority (PRTYS_THREAD, (Tib2Orig.tib2_ulpri & 0x0F00) >> 8,
1178 (UCHAR) Tib2Orig.tib2_ulpri,
1179 0);
1180 }
1181 break;
1182 }
1183
1184 /* we never handle the exception ourselves */
1185
1186 if (Info.ptib->tib_ptib2->tib2_ultid == 1 &&
1187 QtOS2SysXcptMainHandler::libcHandler != NULL)
1188 {
1189 /* we are on the main thread and were installed from qt_init() during
1190 * QApplication initialization using a hack; pass control back to the
1191 * LIBC exception handler */
1192 return QtOS2SysXcptMainHandler::libcHandler (pReportRec, pRegRec,
1193 pContextRec, pv);
1194 }
1195
1196 return XCPT_CONTINUE_SEARCH;
1197}
1198
1199#endif // !defined(QT_PM_NO_SYSEXCEPTIONS)
Note: See TracBrowser for help on using the repository browser.