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

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

Common: System Exceptions:

  • Added recording the word the register value points to in the <Register> tag.
  • Added recording memory dump around EIP for every stack frame using the <Dump> tag inside <Location>.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 28.2 KB
Line 
1/****************************************************************************
2** $Id: qsysxcpt_pm.cpp 145 2006-10-25 23:30:27Z 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 - must be instantiated before a QAplication instance is created
67 (does nothing otherwise)
68*/
69
70/* static */
71bool QtOS2SysXcptMainHandler::installed = FALSE;
72QtOS2SysXcptCallback QtOS2SysXcptMainHandler::callback = NULL;
73ERR QtOS2SysXcptMainHandler::libcHandler = NULL;
74
75/*!
76 @todo (r=dmik) describe...
77*/
78QtOS2SysXcptMainHandler::QtOS2SysXcptMainHandler( QtOS2SysXcptCallback cb )
79{
80 rec.prev_structure = NULL;
81 rec.ExceptionHandler = NULL;
82
83 PTIB ptib = NULL;
84 DosGetInfoBlocks( &ptib, NULL );
85 Q_ASSERT( ptib && ptib->tib_ptib2 );
86
87 if ( ptib && ptib->tib_ptib2 )
88 {
89 // must be instantiated only on the main (first) thread
90 Q_ASSERT( ptib->tib_ptib2->tib2_ultid == 1 );
91 if ( ptib->tib_ptib2->tib2_ultid == 1 )
92 {
93 // must not be already instantiated
94 Q_ASSERT( installed == FALSE );
95 Q_ASSERT( libcHandler == NULL );
96 if ( installed == FALSE && libcHandler == NULL )
97 {
98 installed = TRUE;
99 callback = cb;
100 // install the exception handler for the main thread
101 rec.ExceptionHandler = handler;
102 DosSetExceptionHandler( &rec );
103 }
104 }
105 }
106}
107
108/*!
109 @todo (r=dmik) describe...
110*/
111QtOS2SysXcptMainHandler::~QtOS2SysXcptMainHandler()
112{
113 Q_ASSERT( rec.ExceptionHandler == handler || rec.ExceptionHandler == NULL );
114 if ( rec.ExceptionHandler == handler )
115 {
116 // uninstall the exception handler for the main thread
117 DosUnsetExceptionHandler( &rec );
118 rec.ExceptionHandler = NULL;
119 callback = NULL;
120 installed = FALSE;
121 }
122}
123
124static void qt_excEscapeString( FILE *file, const char *pcszStr );
125
126#define XcptPvt QtOS2SysXcptMainHandler::Private
127
128class XcptPvt
129{
130public:
131 static void callbackWriter( const char *msg );
132
133 static inline int askCallback( QtOS2SysXcptReq req )
134 {
135 if ( callback )
136 return callback( req, NULL, 0 );
137 return FALSE;
138 }
139
140 static inline int letCallback( QtOS2SysXcptReq req )
141 {
142 if ( callback )
143 return callback( req, callbackWriter, 0 );
144 return FALSE;
145 }
146
147 static FILE *file;
148};
149
150/* static */
151FILE *XcptPvt::file = NULL;
152
153/* static */
154void XcptPvt::callbackWriter( const char *msg )
155{
156 if ( file )
157 qt_excEscapeString( file, msg );
158}
159
160/*! \internal
161 Writes the given string with [<>&'"] characters escaped for XML.
162*/
163static void qt_excEscapeString( FILE *file, const char *pcszStr )
164{
165 const char *pcszChars = "<>&'\"";
166 const char *aszEntities[] = { "&lt;", "&gt;", "&amp;", "&apos;", "&quot;" };
167
168 const char *pcsz = pcszStr;
169 const char *pcszRepl = NULL;
170 size_t cbLen = 0;
171
172 if ( !pcsz )
173 return;
174
175 while( *pcsz )
176 {
177 cbLen = strcspn( pcsz, pcszChars );
178 fwrite( pcsz, 1, cbLen, file );
179 pcsz += cbLen;
180 if ( !*pcsz )
181 break;
182 cbLen = strchr( pcszChars, *pcsz ) - pcszChars;
183 pcszRepl = aszEntities[cbLen];
184 fwrite( pcszRepl, 1, strlen(pcszRepl), file );
185 ++ pcsz;
186 }
187}
188
189/*! \internal
190 Writes the given error message.
191*/
192static void qt_excWriteErrorMsg( FILE *file, ULONG ulIndent, const char *pcszMsg,
193 ... )
194{
195 char szBuf[1024];
196 va_list args;
197 va_start( args, pcszMsg );
198 fprintf( file, "%*s<Error message=\"", ulIndent, "" );
199 vsnprintf( szBuf, sizeof(szBuf), pcszMsg, args );
200 szBuf[sizeof(szBuf) - 1] = '\0';
201 qt_excEscapeString( file, szBuf );
202 fprintf( file, "\"/>\n" );
203 va_end( args );
204}
205
206/*! \internal
207 Writes the register name, value and optionally memory flags
208*/
209static void qt_excWriteReg( FILE *file, const char *pszName, ULONG ulValue,
210 BOOL bQueryMem = TRUE )
211{
212 fprintf( file, " <Register name=\"%s\" value=\"%08lX\"",
213 pszName, ulValue );
214
215 if ( bQueryMem )
216 {
217 APIRET arc;
218 ULONG ulCount = 4;
219 ULONG ulFlags = 0;
220 arc = DosQueryMem( (PVOID) ulValue, &ulCount, &ulFlags );
221
222 if ( arc == NO_ERROR || arc == ERROR_INVALID_ADDRESS )
223 {
224 if ( arc == NO_ERROR )
225 {
226 fprintf( file, " flags=\"%08lX\"", ulFlags );
227 if ( ulFlags & (PAG_COMMIT | PAG_READ) == (PAG_COMMIT | PAG_READ) )
228 fprintf( file, " word=\"%08lX\"/>\n", *(ULONG *) ulValue );
229 else
230 fprintf( file, "/>\n" );
231 }
232 else
233 fprintf( file, " flags=\"invalid\"/>\n" );
234 }
235 else
236 {
237 fprintf( file, ">\n" );
238 qt_excWriteErrorMsg( file, 6, "DosQueryMem returned %lu"
239 "and flags %08lX", arc, ulFlags );
240 fprintf( file, " </Register>\n" );
241 }
242 }
243 else
244 fprintf( file, "/>\n" );
245}
246
247/*! \internal
248 Writes information about a signle stack frame.
249*/
250static void qt_excWriteStackFrame( FILE *file, ULONG ulPointer, ULONG ulAddress )
251{
252 APIRET arc = NO_ERROR;
253 HMODULE hMod = NULLHANDLE;
254 char szMod[CCHMAXPATH] = "unknown";
255 ULONG ulObject = 0,
256 ulOffset = 0;
257
258 if ( ulPointer )
259 fprintf( file, " <Frame pointer=\"%08lX\">\n", ulPointer );
260 else
261 fprintf( file, " <Frame pointer=\"current\">\n" );
262
263 fprintf( file, " <Location address=\"%08lX\">\n", ulAddress );
264
265 arc = DosQueryModFromEIP( &hMod, &ulObject,
266 sizeof(szMod), szMod, &ulOffset,
267 ulAddress );
268
269 if (arc != NO_ERROR)
270 qt_excWriteErrorMsg( file, 7, "%s: DosQueryModFromEIP returned %lu",
271 szMod, arc );
272 else
273 {
274 DosQueryModuleName( hMod, sizeof(szMod), szMod );
275 fprintf( file, " <Module ID=\"%04lX\" name=\"", hMod );
276 qt_excEscapeString( file, szMod );
277 fprintf( file, "\" \n"
278 " segment=\"%04lX\" offset=\"%08lX\"/>\n",
279 ulObject + 1, ulOffset );
280/// @todo (r=dmik) use .DBG and .SYM files to get symbols
281// (see debug.h and debug.c from the xwphelpers package)
282// dbgPrintStackFrame(file,
283// szFullName,
284// ulObject,
285// ulOffset);
286 }
287
288 {
289 enum { enmDelta = 8 };
290 UCHAR *pch = (UCHAR *) ulAddress - enmDelta;
291 UCHAR *pchEnd = (UCHAR *) ulAddress + enmDelta - 1;
292 ULONG ulCount = enmDelta * 2;
293 ULONG ulFlags = 0;
294 APIRET arc = NO_ERROR;
295 while ( 1 )
296 {
297 arc = DosQueryMem( (void *) (ULONG) pch, &ulCount, &ulFlags );
298 if ( arc == NO_ERROR )
299 {
300 if ( ulCount >= enmDelta * 2 )
301 break;
302 if ( pch + ulCount <= (UCHAR *) ulAddress )
303 { // ulAddress is outside the pch object
304 pch += ulCount;
305 ulCount = enmDelta * 2 - ulCount;
306 }
307 else
308 { // ulAddress is within the pch object
309 pchEnd = pch += ulCount;
310 break;
311 }
312 }
313 else if ( arc == ERROR_INVALID_ADDRESS )
314 {
315 if ( ((ULONG) pch) & 0xFFFFF000 == ulAddress & 0xFFFFF000 )
316 break; // the same page, ulAddress inaccessible
317 pch = (UCHAR *) (ulAddress & 0xFFFFF000);
318 }
319 }
320 fprintf( file, " <Dump address=\"%08lX\">\n ", pch );
321 if ( arc == NO_ERROR &&
322 ulFlags & (PAG_COMMIT|PAG_READ) == (PAG_COMMIT | PAG_READ) )
323 {
324 for ( ; pch < pchEnd; ++pch )
325 fprintf( file, "%02lX%c", (ULONG) *pch,
326 ulAddress - (ULONG) pch == 1 ? '-' : ' ' );
327 fprintf( file, "\n" );
328 }
329 else
330 qt_excWriteErrorMsg( file, 0, "%08lX: DosQueryMem returned %lu"
331 "and flags %08lX", pch, arc, ulFlags );
332 fprintf( file, " </Dump>\n" );
333 }
334
335 fprintf( file, " </Location>\n"
336 " </Frame>\n" );
337}
338
339/*! \internal
340 Walks the stack and writes information about stack frames.
341*/
342static void qt_excWriteStackFrames( FILE *file, PTIB ptib,
343 PCONTEXTRECORD pContextRec )
344{
345 PULONG pulStackWord = 0;
346
347 fprintf( file, " <Frames>\n" );
348
349 // first the trapping address itself
350 qt_excWriteStackFrame( file, 0, pContextRec->ctx_RegEip );
351
352 pulStackWord = (PULONG) pContextRec->ctx_RegEbp;
353
354 while ( pulStackWord != 0
355 && pulStackWord < (PULONG) ptib->tib_pstacklimit )
356 {
357 if ( ((ULONG) pulStackWord & 0x00000FFF) == 0x00000000 )
358 {
359 // we're on a page boundary: check access
360 ULONG ulCount = 0x1000;
361 ULONG ulFlags = 0;
362 APIRET arc = DosQueryMem( (void *) pulStackWord,
363 &ulCount, &ulFlags );
364 if ( (arc != NO_ERROR)
365 || ( arc == NO_ERROR
366 && (ulFlags & (PAG_COMMIT|PAG_READ))
367 != (PAG_COMMIT|PAG_READ)) )
368 {
369 fprintf( file, " <Frame pointer=\"%08lX\">\n",
370 (ULONG) pulStackWord );
371 qt_excWriteErrorMsg( file, 6, "DosQueryMem returned %lu "
372 "and flags %08lX", arc, ulFlags );
373 fprintf( file, " </Frame>\n" );
374 pulStackWord += 0x1000;
375 continue; // while
376 }
377 }
378
379 qt_excWriteStackFrame( file, (ULONG) pulStackWord, *(pulStackWord + 1) );
380 pulStackWord = (PULONG) *(pulStackWord);
381 } // end while
382
383 fprintf( file, " </Frames>\n" );
384}
385
386/*! \internal
387 Writes the thread information.
388*/
389static void qt_excWriteThreadInfo( FILE *file, PTIB ptib,
390 PEXCEPTIONREPORTRECORD pReportRec,
391 PCONTEXTRECORD pContextRec )
392{
393 ULONG ul = 0;
394 ULONG ulOldPriority = ~0;
395
396 // raise this thread's priority, because this
397 // might take some time
398 ulOldPriority = ptib->tib_ptib2->tib2_ulpri;
399 DosSetPriority( PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0 );
400
401 fprintf( file, " <Thread ID=\"%04lX\" slot=\"%04lX\" "
402 "priority=\"%04lX\" "
403 "mc=\"%04lX\" mcf=\"%04lX\">\n",
404 ptib->tib_ptib2->tib2_ultid, ptib->tib_ordinal, ulOldPriority,
405 ptib->tib_ptib2->tib2_usMCCount, ptib->tib_ptib2->tib2_fMCForceFlag );
406
407 // *** generic exception info
408
409 fprintf( file,
410 " <Exception type=\"%08lX\" flags=\"%08lX\" address=\"%08lX\">\n",
411 pReportRec->ExceptionNum, pReportRec->fHandlerFlags,
412 (ULONG) pReportRec->ExceptionAddress );
413
414 for ( ul = 0; ul < pReportRec->cParameters; ++ul )
415 {
416 fprintf( file, " <Param value=\"%08lX\"/>\n",
417 pReportRec->ExceptionInfo[ul] );
418 }
419
420 fprintf( file, " </Exception>\n" );
421
422 // *** registers
423
424 fprintf( file, " <CPU>\n"
425 " <Registers>\n" );
426
427 if ( pContextRec->ContextFlags & CONTEXT_SEGMENTS )
428 {
429 qt_excWriteReg( file, "DS", pContextRec->ctx_SegDs, FALSE );
430 qt_excWriteReg( file, "ES", pContextRec->ctx_SegEs, FALSE );
431 qt_excWriteReg( file, "FS", pContextRec->ctx_SegFs, FALSE );
432 qt_excWriteReg( file, "GS", pContextRec->ctx_SegGs, FALSE );
433 }
434
435 if ( pContextRec->ContextFlags & CONTEXT_INTEGER )
436 {
437 qt_excWriteReg( file, "EAX", pContextRec->ctx_RegEax );
438 qt_excWriteReg( file, "EBX", pContextRec->ctx_RegEbx );
439 qt_excWriteReg( file, "ECX", pContextRec->ctx_RegEcx );
440 qt_excWriteReg( file, "EDX", pContextRec->ctx_RegEdx );
441 qt_excWriteReg( file, "ESI", pContextRec->ctx_RegEsi );
442 qt_excWriteReg( file, "EDI", pContextRec->ctx_RegEdi );
443 }
444
445 if ( pContextRec->ContextFlags & CONTEXT_CONTROL )
446 {
447 qt_excWriteReg( file, "CS", pContextRec->ctx_SegCs, FALSE );
448 qt_excWriteReg( file, "EIP", pContextRec->ctx_RegEip );
449 qt_excWriteReg( file, "SS", pContextRec->ctx_SegSs, FALSE );
450 qt_excWriteReg( file, "ESP", pContextRec->ctx_RegEsp );
451 qt_excWriteReg( file, "EBP", pContextRec->ctx_RegEbp );
452 qt_excWriteReg( file, "EFLAGS", pContextRec->ctx_EFlags, FALSE );
453 }
454
455 fprintf( file, " </Registers>\n"
456 " </CPU>\n" );
457
458 // *** stack
459
460 fprintf( file, " <Stack base=\"%08lX\" limit=\"%08lX\">\n",
461 (ULONG) ptib->tib_pstack,
462 (ULONG) ptib->tib_pstacklimit );
463
464 if ( pContextRec->ContextFlags & CONTEXT_CONTROL )
465 {
466 qt_excWriteStackFrames( file, ptib, pContextRec );
467 }
468
469 fprintf( file, " </Stack>\n"
470 " </Thread>\n" );
471
472 // reset old priority
473 DosSetPriority( PRTYS_THREAD, (ulOldPriority & 0x0F00) >> 8,
474 (UCHAR) ulOldPriority,
475 0 );
476}
477
478/*! \internal
479 Writes exception information to the log file.
480*/
481static void qt_excWriteException( FILE *file,
482 const char *pszExeName,
483 const char *pszExeBase,
484 PPIB ppib, PTIB ptib,
485 PEXCEPTIONREPORTRECORD pReportRec,
486 PCONTEXTRECORD pContextRec )
487{
488 ULONG aulBuf[3];
489
490 // *** application info
491
492 {
493 fprintf( file, " <Application name=\"" );
494 if ( XcptPvt::askCallback( QtOS2SysXcptReq_AppName ) == TRUE )
495 XcptPvt::letCallback( QtOS2SysXcptReq_AppName );
496 else
497 qt_excEscapeString( file, pszExeBase );
498 fprintf( file, "\" version=\"" );
499 if ( XcptPvt::askCallback( QtOS2SysXcptReq_AppVer ) == TRUE )
500 XcptPvt::letCallback( QtOS2SysXcptReq_AppVer );
501 else
502 fprintf( file, "unknown" );
503 fprintf( file, "\">\n" );
504
505 if ( XcptPvt::askCallback( QtOS2SysXcptReq_ReportTo ) == TRUE )
506 {
507 fprintf( file, " <Report to=\"" );
508 XcptPvt::letCallback( QtOS2SysXcptReq_ReportTo );
509 if ( XcptPvt::askCallback( QtOS2SysXcptReq_ReportSubj ) == TRUE )
510 {
511 fprintf( file, "\" subject=\"" );
512 XcptPvt::letCallback( QtOS2SysXcptReq_ReportSubj );
513 }
514 fprintf( file, "\"/>\n" );
515 }
516
517 fprintf( file, " </Application>\n" );
518 }
519
520 // *** system info
521
522 DosQuerySysInfo( QSV_VERSION_MAJOR, QSV_VERSION_REVISION,
523 &aulBuf, sizeof(aulBuf) );
524 // Warp 3 is reported as 20.30
525 // Warp 4 is reported as 20.40
526 // Aurora is reported as 20.45
527
528 fprintf( file,
529 " <System name=\"OS/2\" version=\"%u.%u.%u\"/>\n",
530 aulBuf[0], aulBuf[1], aulBuf[2] );
531
532 // *** process info
533
534 if ( ppib )
535 {
536 fprintf( file,
537 " <Process ID=\"%04lX\" parentID=\"%04lX\">\n"
538 " <Module ID=\"%04lX\" name=\"",
539 ppib->pib_ulpid, ppib->pib_ulppid, ppib->pib_hmte );
540 qt_excEscapeString( file, pszExeName );
541 fprintf( file,
542 "\"/>\n"
543 " </Process>\n" );
544 }
545 else
546 qt_excWriteErrorMsg( file, 1, "ppib is NULL" );
547
548 fprintf( file, " <Threads>\n" );
549
550 if ( ptib && ptib->tib_ptib2 )
551 qt_excWriteThreadInfo( file, ptib, pReportRec, pContextRec );
552 else if ( !ptib )
553 qt_excWriteErrorMsg( file, 1, "ptib is NULL" );
554 else
555 qt_excWriteErrorMsg( file, 1, "ptib->tib_ptib2 is NULL" );
556
557 fprintf( file, " </Threads>\n" );
558}
559
560/**
561 * Opens an unique log file using \a pcszBasePath as the base path.
562 * On input, \a pszFileName is the desired base file name w/o extension.
563 * If it doesn't fit into the file name length limit (ENAMETOOLONG) for the
564 * FS of the given disk, then a 8x3 file name is attemptet as a fallback.
565 * On output, \a pszFileName will contain the full file name of the opened
566 * log file. Note that \a pszFileName must be at least CCHMAXPATH bytes length.
567 */
568static FILE *qt_excOpenLogFile( const char *pcszBasePath, char *pszFileName )
569{
570 FILE *file = NULL;
571
572 char szFileName[CCHMAXPATH];
573 char szDir[] = "\\.qt3traps";
574 char szFile[] = "\\12345678.123";
575 enum { cbFileName = sizeof(szFileName) };
576 enum { cbDir = sizeof(szDir) };
577 enum { cbFile = sizeof(szFile) };
578
579 ULONG ul = 0;
580 ULONG cbDirLen = 0;
581 ULONG cbLen = 0;
582 ULONG ulStamp = 0;
583 char *pszStamp = NULL;
584
585 if ( pcszBasePath == NULL || pszFileName == NULL )
586 return NULL;
587
588 if ( access( pcszBasePath, F_OK ) != 0 )
589 return NULL;
590
591 if ( strlen( pcszBasePath ) + cbDir + cbFile - 2 >= CCHMAXPATH )
592 return NULL;
593
594 // get the full path if it's not 'X:'
595 if ( strlen(pcszBasePath) != 2 ||
596 pcszBasePath[1] != ':' )
597 {
598 if( DosQueryPathInfo( pcszBasePath, FIL_QUERYFULLNAME,
599 szFileName, cbFileName ) != NO_ERROR )
600 return NULL;
601 }
602 else
603 strcpy( szFileName, pcszBasePath );
604
605 strcat( szFileName, szDir );
606 if ( access( szFileName, F_OK ) != 0 )
607 if ( mkdir( szFileName, 0777 ) != 0 )
608 return NULL;
609
610 cbDirLen = strlen( szFileName );
611
612 // first, try to use the desired base file name
613
614 // we will append -NN to the file name to add some 'fraction of a
615 // second' granularity to avoid name conflicts (0 <= NNN <= 99)
616 cbLen = strlen( pszFileName );
617 if ( cbDirLen + cbLen + 8 /* \-NN.xml */ < CCHMAXPATH )
618 {
619 strcat( szFileName, "\\" );
620 strcat( szFileName, pszFileName );
621 cbLen += cbDirLen + 1 /* \ */;
622 for( ul = 0; ul < 100; ++ul )
623 {
624 sprintf( szFileName + cbLen, "-%02ld.xml", ul );
625 if ( access( szFileName, F_OK ) == 0 )
626 continue;
627 file = fopen( szFileName, "wt" );
628 if ( file )
629 break;
630 if ( errno == ENAMETOOLONG )
631 break;
632 }
633 }
634
635 // next, try a time stamp as a 8x3 file name
636 if ( file == NULL )
637 {
638 pszStamp = szFileName + cbDirLen + 1;
639 strcat( szFileName, szFile );
640
641 ulStamp = time( NULL );
642
643 // In order to add some 'fraction of a second' granularity to the
644 // timestamp, we shift it by 5 bits. This gives us 0x07FFFFFF seconds
645 // which is a period of approx. 4,25 years. 5 bits in turn give us 32
646 // fractions of a second.
647 ulStamp <<= 5;
648
649 // try some few adajcent stamps if the first one fails
650 for ( ul = 0; ul < 32; ++ul, ++ulStamp )
651 {
652 sprintf( pszStamp, "%08lX.xml", ulStamp );
653 if ( access( szFileName, F_OK ) == 0 )
654 continue;
655 file = fopen( szFileName, "wt" );
656 if ( file )
657 break;
658 }
659 }
660
661 if ( file )
662 {
663 strncpy( pszFileName, szFileName, CCHMAXPATH - 1 );
664 pszFileName[CCHMAXPATH - 1] = '\0';
665 }
666
667 return file;
668}
669
670/*! \internal
671 Qt Exception handler.
672*/
673/* static */
674ULONG APIENTRY
675QtOS2SysXcptMainHandler::handler( PEXCEPTIONREPORTRECORD pReportRec,
676 PEXCEPTIONREGISTRATIONRECORD pRegRec,
677 PCONTEXTRECORD pContextRec,
678 PVOID pv )
679{
680 PTIB ptib = NULL;
681 PPIB ppib = NULL;
682
683 /* From the VAC++3 docs:
684 * "The first thing an exception handler should do is check the
685 * exception flags. If EH_EXIT_UNWIND is set, meaning
686 * the thread is ending, the handler tells the operating system
687 * to pass the exception to the next exception handler. It does the
688 * same if the EH_UNWINDING flag is set, the flag that indicates
689 * this exception handler is being removed.
690 * The EH_NESTED_CALL flag indicates whether the exception
691 * occurred within an exception handler. If the handler does
692 * not check this flag, recursive exceptions could occur until
693 * there is no stack remaining."
694 *
695 * So for all these conditions, we exit immediately.
696 */
697
698 if ( pReportRec->fHandlerFlags &
699 (EH_EXIT_UNWIND | EH_UNWINDING | EH_NESTED_CALL) )
700 return XCPT_CONTINUE_SEARCH;
701
702 // can it ever fail?...
703 DosGetInfoBlocks( &ptib, &ppib );
704
705 switch (pReportRec->ExceptionNum)
706 {
707 case XCPT_ACCESS_VIOLATION:
708 case XCPT_INTEGER_DIVIDE_BY_ZERO:
709 case XCPT_ILLEGAL_INSTRUCTION:
710 case XCPT_PRIVILEGED_INSTRUCTION:
711 case XCPT_INVALID_LOCK_SEQUENCE:
712 case XCPT_INTEGER_OVERFLOW:
713 {
714 // "real" exceptions:
715
716 DATETIME dt;
717 char szFileName[CCHMAXPATH];
718 FILE *file = NULL;
719 char szExeName[CCHMAXPATH] = "unknown";
720 char szExeBase[CCHMAXPATH];
721
722 DosBeep( 880, 200 );
723
724 if ( ppib )
725 {
726 // get the main module name
727 DosQueryModuleName( ppib->pib_hmte, sizeof(szExeName),
728 szExeName );
729
730 // find the base name (w/o path and extension)
731 char *psz = strrchr( szExeName, '\\' );
732 if ( psz )
733 ++ psz;
734 if ( !psz )
735 psz = szExeName;
736 strcpy( szExeBase, psz );
737 psz = strrchr( szExeBase, '.' );
738 if ( psz )
739 *psz = '\0';
740 }
741
742 // compose the desired base file name for the log file
743 // (<datetime>-<exe>)
744 DosGetDateTime( &dt );
745 sprintf( szFileName, "%04d%02d%02d%02d%02d%02d-%s",
746 dt.year, dt.month, dt.day,
747 dt.hours, dt.minutes, dt.seconds,
748 szExeBase );
749
750 // open the log file:
751
752 // first try the home directory, then the root drive
753 // (see QDir::homeDirPath())
754 file = qt_excOpenLogFile( getenv( "HOME" ), szFileName );
755 if ( file == NULL )
756 {
757 char *pszHomeDrive = getenv( "HOMEDRIVE" );
758 char *pszHomePath = getenv( "HOMEPATH" );
759 if ( pszHomeDrive && pszHomePath &&
760 strlen( pszHomeDrive ) + strlen( pszHomePath ) < CCHMAXPATH )
761 {
762 char szHomePath[CCHMAXPATH];
763 strcpy( szHomePath, pszHomeDrive );
764 strcat( szHomePath, pszHomePath );
765 file = qt_excOpenLogFile( szHomePath, szFileName );
766 }
767 }
768 if ( file == NULL )
769 {
770 static char szBootDrive[] = "\0:";
771 if ( !szBootDrive[0] )
772 {
773 ULONG ulBootDrive;
774 DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
775 &ulBootDrive, sizeof(ulBootDrive) );
776 szBootDrive[0] = (char) ulBootDrive + 'A' - 1;
777 }
778 file = qt_excOpenLogFile( szBootDrive, szFileName );
779 }
780
781 if ( file != NULL )
782 {
783 fprintf( file,
784 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
785 "<!--\n"
786 " A fatal error has occured while running this application.\n"
787 " Please contact the application vendor, describe what happened\n"
788 " and send the contents of this file saved locally as\n"
789 " '%s'.\n"
790 "-->\n",
791 szFileName );
792
793 fprintf( file,
794 "<Trap timestamp=\"%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d\" "
795 "version=\"1.0\"\n"
796 " generator=\"Qt Library Version %s\"\n"
797 " xmlns=\"http://db.hugaida.com/xml/os2/sys/Trap\">\n",
798 dt.year, dt.month, dt.day,
799 dt.hours, dt.minutes, dt.seconds,
800 (dt.timezone > 0 ? '-' : '+'),
801 abs( dt.timezone / 60 ),
802 abs( dt.timezone % 60 ),
803 QT_VERSION_STR );
804
805 XcptPvt::file = file;
806
807 // write trap information
808 qt_excWriteException( file, szExeName, szExeBase,
809 ppib, ptib, pReportRec, pContextRec );
810
811 XcptPvt::file = NULL;
812
813 fprintf( file, "</Trap>\n\n" );
814 fclose( file );
815
816 // attempt to display the created file
817 {
818 STARTDATA SData = {0};
819 PID pid = 0;
820 ULONG ulSessID = 0;
821
822 SData.Length = sizeof(STARTDATA);
823 SData.PgmName = "E.EXE";
824 SData.PgmInputs = szFileName;
825
826 DosStartSession( &SData, &ulSessID, &pid );
827 }
828 }
829 else
830 DosBeep( 220, 200 );
831 }
832 break;
833 }
834
835 // we never handle the exception ourselves
836
837 if ( ptib && ptib->tib_ptib2 && ptib->tib_ptib2->tib2_ultid == 1 &&
838 QtOS2SysXcptMainHandler::libcHandler != NULL )
839 {
840 // we are on the main thread and were installed from qt_init() during
841 // QApplication initialization using a hack; pass control back to the
842 // LIBC exception handler
843 return QtOS2SysXcptMainHandler::libcHandler( pReportRec, pRegRec,
844 pContextRec, pv );
845 }
846
847 return XCPT_CONTINUE_SEARCH;
848}
849
850#endif // !defined(QT_PM_NO_SYSEXCEPTIONS)
Note: See TracBrowser for help on using the repository browser.