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

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

Common: System Exceptions: Removed an extra line break.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 49.3 KB
Line 
1/****************************************************************************
2** $Id: qsysxcpt_pm.cpp 158 2006-12-03 17:22:24Z 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/// @todo (r=dmik) use plain DosWrite() and friends instead of fprintf() and
45// friends (LIBC may be in a inconsistent state at the point when an exception
46// is thrown (see kLIBC's logstrict.c for some sprintf like stuff)
47
48#if !defined(QT_OS2_NO_SYSEXCEPTIONS)
49
50#include "qt_os2.h"
51
52#include <stdio.h>
53#include <stdlib.h>
54#include <stdarg.h>
55#include <unistd.h>
56#include <string.h>
57#include <time.h>
58#include <errno.h>
59#include <sys/stat.h>
60
61/** @class QtOS2SysXcptMainHandler qt_os2.h
62 *
63 * The QtOS2SysXcptMainHandler class is used to install the OS2/ system
64 * exception handler on the main thread of the Qt application.
65 *
66 * The installed exception handler will catch most fatal exceptions (commonly
67 * referred to as traps), such as memory access violation, and create a
68 * detailed report about the state of the failed application (process name,
69 * loaded modules, thread states and call stacks). The created report is saved
70 * in the user home directory and is supposed to be sent to the application
71 * developer (which makes it much easier to find a reason of the trap and
72 * therefore gives a better chance that the problem will be fixed).
73 *
74 * In order to install the exception handler, an instance of this class is
75 * created on the stack of the main thread. The best place for it is the
76 * first line of the application's main() function, before doing anything else.
77 * One doesn't need (and <b>should not</b>) create a QApplication instance
78 * before installing the exception handler. If you do so, then QApplication
79 * will install the same exception handler on its own, which is not a preferred
80 * way for two reasons:
81 * <ul>
82 * <li>QApplication uses a hack to attach the exception handler to the
83 * exception handler installed by the compiler's C Library (LIBC). This
84 * hack will not work if there is no exception handler provided by LIBC
85 * or it may work incorrectly if there are other exception handlers
86 * installed after it using the same (or similar hack).
87 * <li>There is no way to specify the exception handler callback (see below).
88 * </ul>
89 *
90 * There may be only one instance of this class and it must be created only on
91 * the main (UI) thread. All other threads started by Qt using the QThread
92 * class will have the exception handler installed authomatically before the
93 * QThread::run() method starts execution, so there is no need to install it
94 * explicitly.
95 *
96 * Note that QtOS2SysXcptMainHandler is a stack-based class, which means that
97 * you cannot create an instance using the new operator (you will get a
98 * compile time error). You might want to create it as a static variable (for
99 * example, if you want to catch exceptions happening before main() is entered,
100 * such as initialization of other static variables), however this is not
101 * currently supported and may work incorrectly because the OS/2 system
102 * documentation clearly states that the exception handler data must be
103 * allocated on the stack. Here is a typical usage pattern for installing the
104 * exception handler:
105 *
106 * \code
107 * int main (int argc, char **argv)
108 * {
109 * QtOS2SysXcptMainHandler sysXcptHandler( psiOS2SysXcptCallback );
110 *
111 * QApplication app;
112 * MyWidget wgt;
113 * app.setMainWidget (&wgt);
114 * wgt.show();
115 * return app.exec();
116 * }
117 * \endcode
118 *
119 * The exception report is an XML file with the well-defined structure and
120 * contains enough information to find symbols corresponding to execution
121 * points on the call stack (provided that there is a .SYM file for a program
122 * or a library module). This file is always created in the SysTraps
123 * subdirectory of the user's home directory (or of the root directory of the
124 * boot drive if no home directory exists). Currently there is no way to
125 * specify a different location. A separate report file is created per every
126 * trap.
127 *
128 * In order to store application-specific information in the report file,
129 * an application can install the exception handler callback that is
130 * called at certain points during exception handling. This callback is
131 * specified as an argument to the QtOS2SysXcptMainHandler constructor, but
132 * it will be called on any Qt thread that caught an exception, not
133 * necessarily the main one.
134 *
135 * The callback function has the following prototype:
136 * \code
137 * int XcptCallback( QtOS2SysXcptReq req, QtOS2SysXcptWriter writer, int reserved );
138 * \endcode
139 * \a req is one of the QtOS2SysXcptReq values describing what type of
140 * information the exception handler wants to get from the callback, \a writer
141 * is the function used by the callback to write the requested information to
142 * the report file and \a reserved is a reserved value which is currently
143 * always zero.
144 *
145 * The callback can be called by the exception handler in two modes: \em ask
146 * mode or \em let mode. In the "ask" mode (indicated by the NULL \a writer
147 * argument), the handler asks whether the callback supports the requested
148 * bit of information, as defined by the \a req argument. In the "let" mode
149 * (where \a writer is not NULL), it lets the callback to write the requested
150 * information to the report file using the supplied \a writer. Note that the
151 * callback should not deal with any structural report details (such as XML
152 * tags or character escaping) -- the string it writes is just an arbitrary
153 * character value for the requested attribute. The meaning of different
154 * QtOS2SysXcptReq values can be easily explained with an example:
155 *
156 * \code
157 QString PROG_NAME = "MyApp";
158 QString PROG_VERSION = "0.1";
159
160 int psiOS2SysXcptCallback( QtOS2SysXcptReq req, QtOS2SysXcptWriter writer, int )
161 {
162 switch( req )
163 {
164 // application name
165 case QtOS2SysXcptReq_AppName:
166 if ( writer ) writer( PROG_NAME.latin1() );
167 return TRUE;
168
169 // application version
170 case QtOS2SysXcptReq_AppVer:
171 if ( writer ) writer( PROG_VERSION.latin1() );
172 return TRUE;
173
174 // e-mail to send generated trap reports to
175 // (can be used by trap report handling utilities to assist the
176 // user to compose and send trap reports to the developer)
177 case QtOS2SysXcptReq_ReportTo:
178 if ( writer ) writer( "my-traps@some.where" );
179 return TRUE;
180
181 // recommended subject of the generated e-mail message
182 // (same purpose as above)
183 case QtOS2SysXcptReq_ReportSubj:
184 // we don't need to provide this kind of information,
185 // so simply return FALSE
186 return TRUE;
187
188 default:
189 break;
190 }
191
192 // we don't support any other information requests
193 return FALSE;
194 }
195 * \endcode
196 */
197
198/* static */
199bool QtOS2SysXcptMainHandler::installed = FALSE;
200QtOS2SysXcptCallback QtOS2SysXcptMainHandler::callback = NULL;
201ERR QtOS2SysXcptMainHandler::libcHandler = NULL;
202
203/**
204 * Installs the exception handler on the main thread and registers \a cb
205 * as the exception handler callback. If \a cb is NULL (by default), then no
206 * user-supplied exception callback will be registered.
207 */
208QtOS2SysXcptMainHandler::QtOS2SysXcptMainHandler (QtOS2SysXcptCallback cb)
209{
210 rec.prev_structure = NULL;
211 rec.ExceptionHandler = NULL;
212
213 PTIB ptib = NULL;
214 DosGetInfoBlocks (&ptib, NULL);
215 Q_ASSERT (ptib && ptib->tib_ptib2);
216
217 if (ptib && ptib->tib_ptib2)
218 {
219 /* must be instantiated only on the main (first) thread */
220 Q_ASSERT (ptib->tib_ptib2->tib2_ultid == 1);
221 if (ptib->tib_ptib2->tib2_ultid == 1)
222 {
223 /* must not be already instantiated */
224 Q_ASSERT (installed == FALSE);
225 Q_ASSERT (libcHandler == NULL);
226 if (installed == FALSE && libcHandler == NULL)
227 {
228 installed = TRUE;
229 callback = cb;
230 /* install the exception handler for the main thread */
231 rec.ExceptionHandler = handler;
232 DosSetExceptionHandler (&rec);
233 }
234 }
235 }
236}
237
238/**
239 * Uninstalls the exception handler installed by the constructor.
240 */
241QtOS2SysXcptMainHandler::~QtOS2SysXcptMainHandler()
242{
243 Q_ASSERT (rec.ExceptionHandler == handler || rec.ExceptionHandler == NULL);
244 if (rec.ExceptionHandler == handler)
245 {
246 /* uninstall the exception handler for the main thread */
247 DosUnsetExceptionHandler (&rec);
248 rec.ExceptionHandler = NULL;
249 callback = NULL;
250 installed = FALSE;
251 }
252}
253
254static void qt_excEscapeString (FILE *file, const char *pcszStr);
255
256#define XcptPvt QtOS2SysXcptMainHandler::Private
257
258class XcptPvt
259{
260public:
261 static void callbackWriter (const char *msg);
262
263 static inline int askCallback (QtOS2SysXcptReq req)
264 {
265 if (callback)
266 return callback (req, NULL, 0);
267 return FALSE;
268 }
269
270 static inline int letCallback (QtOS2SysXcptReq req)
271 {
272 if (callback)
273 return callback (req, callbackWriter, 0);
274 return FALSE;
275 }
276
277 static FILE *file;
278};
279
280/* static */
281FILE *XcptPvt::file = NULL;
282
283/* static */
284void XcptPvt::callbackWriter (const char *msg)
285{
286 if (file)
287 qt_excEscapeString (file, msg);
288}
289
290/** \internal
291 * Writes the given string with [<>&'"] characters escaped for XML.
292 */
293static void qt_excEscapeString (FILE *file, const char *pcszStr)
294{
295 const char *pcszChars = "<>&'\"";
296 const char *aszEntities[] = { "&lt;", "&gt;", "&amp;", "&apos;", "&quot;" };
297
298 const char *pcsz = pcszStr;
299 const char *pcszRepl = NULL;
300 size_t cbLen = 0;
301
302 if (!pcsz)
303 return;
304
305 while (*pcsz)
306 {
307 cbLen = strcspn (pcsz, pcszChars);
308 fwrite (pcsz, 1, cbLen, file);
309 pcsz += cbLen;
310 if (!*pcsz)
311 break;
312 cbLen = strchr (pcszChars, *pcsz) - pcszChars;
313 pcszRepl = aszEntities[cbLen];
314 fwrite (pcszRepl, 1, strlen(pcszRepl), file);
315 ++ pcsz;
316 }
317}
318
319/** \internal
320 * Writes the given error message.
321 */
322static void qt_excWriteErrorMsg (FILE *file, ULONG ulIndent, const char *pcszMsg,
323 ...)
324{
325 char szBuf[1024];
326 va_list args;
327 va_start (args, pcszMsg);
328 fprintf (file, "%*s<Error message=\"", ulIndent, "");
329 vsnprintf (szBuf, sizeof(szBuf), pcszMsg, args);
330 szBuf[sizeof(szBuf) - 1] = '\0';
331 qt_excEscapeString (file, szBuf);
332 fprintf (file, "\"/>\n");
333 va_end (args);
334}
335
336/** \internal
337 * Writes the register name, value and optionally memory flags
338 */
339static void qt_excWriteReg (FILE *file, const char *pszName, ULONG ulValue,
340 BOOL bQueryMem = TRUE)
341{
342 fprintf (file, " <Register name=\"%s\" value=\"%08lX\"",
343 pszName, ulValue);
344
345 if (bQueryMem)
346 {
347 APIRET arc;
348 ULONG ulCount = 4;
349 ULONG ulFlags = 0;
350 arc = DosQueryMem ((PVOID) ulValue, &ulCount, &ulFlags);
351
352 if (arc == NO_ERROR || arc == ERROR_INVALID_ADDRESS)
353 {
354 if (arc == NO_ERROR)
355 {
356 fprintf (file, " flags=\"%08lX\"", ulFlags);
357 if ((ulFlags & (PAG_COMMIT | PAG_READ)) ==
358 (PAG_COMMIT | PAG_READ))
359 fprintf (file, " word=\"%08lX\"/>\n", *(ULONG *) ulValue);
360 else
361 fprintf (file, "/>\n");
362 }
363 else
364 fprintf (file, " flags=\"invalid\"/>\n");
365 }
366 else
367 {
368 fprintf (file, ">\n");
369 qt_excWriteErrorMsg (file, 7, "DosQueryMem returned %lu "
370 "and flags %08lX", arc, ulFlags);
371 fprintf (file, " </Register>\n");
372 }
373 }
374 else
375 fprintf (file, "/>\n");
376}
377
378/** \internal
379 * Writes information about a signle stack frame.
380 */
381static void qt_excWriteStackFrame (FILE *file, ULONG ulRegEbp, ULONG ulRegEip)
382{
383 APIRET arc = NO_ERROR;
384 HMODULE hMod = NULLHANDLE;
385 char szMod[CCHMAXPATH] = "";
386 ULONG ulObject = 0,
387 ulOffset = 0;
388
389 fprintf (file, " <Frame pointer=\"%08lX\">\n", ulRegEbp);
390 fprintf (file, " <Location address=\"%08lX\">\n", ulRegEip);
391
392 arc = DosQueryModFromEIP (&hMod, &ulObject,
393 sizeof (szMod), szMod, &ulOffset,
394 ulRegEip);
395
396 if (arc != NO_ERROR)
397 qt_excWriteErrorMsg (file, 8, "DosQueryModFromEIP returned %lu", arc);
398 else
399 fprintf (file, " <Module ID=\"%04lX\" segment=\"%04lX\" "
400 "offset=\"%08lX\"/>\n",
401 hMod, ulObject + 1, ulOffset);
402
403 /* write a small memory dump with the location address in the middle */
404 {
405 enum { enmDelta = 8 };
406 UCHAR *pch = (UCHAR *) ulRegEip - enmDelta;
407 UCHAR *pchEnd = (UCHAR *) ulRegEip + enmDelta - 1;
408 ULONG ulCount = enmDelta * 2;
409 ULONG ulFlags = 0;
410 APIRET arc = NO_ERROR;
411 while (1)
412 {
413 arc = DosQueryMem ((void *) (ULONG) pch, &ulCount, &ulFlags);
414 if (arc == NO_ERROR)
415 {
416 if (ulCount >= enmDelta * 2)
417 break;
418 if (pch + ulCount <= (UCHAR *) ulRegEip)
419 {
420 /* ulAddress is outside the pch object */
421 pch += ulCount;
422 ulCount = enmDelta * 2 - ulCount;
423 }
424 else
425 {
426 /* ulAddress is within the pch object */
427 pchEnd = pch += ulCount;
428 break;
429 }
430 }
431 else if (arc == ERROR_INVALID_ADDRESS)
432 {
433 if ((((ULONG) pch) & 0xFFFFF000) == (ulRegEip & 0xFFFFF000))
434 break; /* the same page, ulAddress inaccessible */
435 pch = (UCHAR *) (ulRegEip & 0xFFFFF000);
436 }
437 }
438 fprintf (file, " <Dump address=\"%08lX\">\n ", pch);
439 if (arc == NO_ERROR &&
440 (ulFlags & (PAG_COMMIT | PAG_READ)) == (PAG_COMMIT | PAG_READ))
441 {
442 for (; pch < pchEnd; ++pch)
443 fprintf (file, "%02lX%c", (ULONG) *pch,
444 ulRegEip - (ULONG) pch == 1 ? '-' : ' ');
445 fprintf (file, "\n");
446 }
447 else
448 qt_excWriteErrorMsg (file, 0, "%08lX: DosQueryMem returned %lu "
449 "and flags %08lX", pch, arc, ulFlags);
450 fprintf (file, " </Dump>\n");
451 }
452
453 fprintf (file, " </Location>\n");
454 fprintf (file, " </Frame>\n");
455}
456
457/** \internal
458 * Walks the stack and writes information about stack frames.
459 * @note This function should be in sync with qt_excWriteModulesOnStack()
460 * because they share the same logic.
461 */
462static void qt_excWriteStackFrames (FILE *file, ULONG ulStackLimit,
463 PCONTEXTRECORD pContextRec)
464{
465 ULONG ulRegEbp = pContextRec->ctx_RegEbp;
466 ULONG ulRegEip = pContextRec->ctx_RegEip;
467
468 fprintf (file, " <Frames>\n");
469
470 /* first the trapping address itself */
471 qt_excWriteStackFrame (file, ulRegEbp, ulRegEip);
472
473 /* first call to qt_excWriteStackFrame() is done before the EBP validity
474 * check below to get a chance to call qt_excWriteStackFrame() for the
475 * trapping address itself even if EBP there is invalid. */
476
477 while (ulRegEbp != 0 && ulRegEbp < ulStackLimit)
478 {
479 /* skip the trapping stack frame -- already written above */
480 if (pContextRec->ctx_RegEbp != ulRegEbp)
481 qt_excWriteStackFrame (file, ulRegEbp, ulRegEip);
482
483 if ((ulRegEbp & 0x00000FFF) == 0x00000000)
484 {
485 /* we're on a page boundary: check access */
486 ULONG ulCount = 0x1000;
487 ULONG ulFlags = 0;
488 APIRET arc = DosQueryMem ((void *) ulRegEbp, &ulCount, &ulFlags);
489 if ( (arc != NO_ERROR)
490 || ( arc == NO_ERROR
491 && (ulFlags & (PAG_COMMIT | PAG_READ))
492 != (PAG_COMMIT | PAG_READ)))
493 {
494 fprintf (file, " <Frame pointer=\"%08lX\">\n", ulRegEbp);
495 qt_excWriteErrorMsg (file, 7, "DosQueryMem returned %lu "
496 "and flags %08lX", arc, ulFlags);
497 fprintf (file, " </Frame>\n");
498 /* try go to the next page */
499 /// @todo (r=dmik) I don't know how much is it accurate,
500 // I've just taken the logic from xwphelpers sources.
501 ulRegEbp += 0x1000;
502 continue; /* while */
503 }
504 }
505
506 /* get the return address of the current call */
507 ulRegEip = *(((PULONG) ulRegEbp) + 1);
508 /* get the address of the outer stack frame */
509 ulRegEbp = *((PULONG) ulRegEbp);
510 }
511
512 fprintf (file, " </Frames>\n");
513}
514
515/** \internal
516 * Writes the thread information.
517 * If ptib is not NULL, the current thread's information is to be written.
518 * If ptib is NULL, pThrdRec is guaranted not to be NULL.
519 */
520static void qt_excWriteThreadInfo (FILE *file, PTIB ptib, QSTREC *pThrdRec,
521 PCONTEXTRECORD pContextRec)
522{
523 ULONG ulStackBase = 0, ulStackLimit = 0;
524 ULONG ulCount = 0, ulFlags = 0;
525 ULONG ul = 0;
526 APIRET arc = NO_ERROR;
527
528 if (ptib)
529 {
530 fprintf (file, " <Thread ID=\"%04lX\" slot=\"%04lX\" "
531 "priority=\"%04lX\" ",
532 ptib->tib_ptib2->tib2_ultid, ptib->tib_ordinal,
533 ptib->tib_ptib2->tib2_ulpri);
534 if (pThrdRec)
535 fprintf (file, "state=\"%02lX\" ", (ULONG) pThrdRec->state);
536 fprintf (file, "mc=\"%04lX\" mcf=\"%04lX\">\n",
537 ptib->tib_ptib2->tib2_usMCCount,
538 ptib->tib_ptib2->tib2_fMCForceFlag);
539 }
540 else
541 {
542 fprintf (file, " <Thread ID=\"%04lX\" slot=\"%04lX\" "
543 "priority=\"%04lX\" state=\"%02lX\">\n",
544 pThrdRec->tid, pThrdRec->slot, pThrdRec->priority,
545 pThrdRec->state);
546 }
547
548 /* *** registers */
549
550 fprintf (file, " <CPU>\n"
551 " <Registers>\n");
552
553 if (pContextRec->ContextFlags & CONTEXT_SEGMENTS)
554 {
555 qt_excWriteReg (file, "DS", pContextRec->ctx_SegDs, FALSE);
556 qt_excWriteReg (file, "ES", pContextRec->ctx_SegEs, FALSE);
557 qt_excWriteReg (file, "FS", pContextRec->ctx_SegFs, FALSE);
558 qt_excWriteReg (file, "GS", pContextRec->ctx_SegGs, FALSE);
559 }
560
561 if (pContextRec->ContextFlags & CONTEXT_INTEGER)
562 {
563 qt_excWriteReg (file, "EAX", pContextRec->ctx_RegEax);
564 qt_excWriteReg (file, "EBX", pContextRec->ctx_RegEbx);
565 qt_excWriteReg (file, "ECX", pContextRec->ctx_RegEcx);
566 qt_excWriteReg (file, "EDX", pContextRec->ctx_RegEdx);
567 qt_excWriteReg (file, "ESI", pContextRec->ctx_RegEsi);
568 qt_excWriteReg (file, "EDI", pContextRec->ctx_RegEdi);
569 }
570
571 if (pContextRec->ContextFlags & CONTEXT_CONTROL)
572 {
573 qt_excWriteReg (file, "CS", pContextRec->ctx_SegCs, FALSE);
574 qt_excWriteReg (file, "EIP", pContextRec->ctx_RegEip);
575 qt_excWriteReg (file, "SS", pContextRec->ctx_SegSs, FALSE);
576 qt_excWriteReg (file, "ESP", pContextRec->ctx_RegEsp);
577 qt_excWriteReg (file, "EBP", pContextRec->ctx_RegEbp);
578 qt_excWriteReg (file, "EFLAGS", pContextRec->ctx_EFlags, FALSE);
579 }
580
581 fprintf (file, " </Registers>\n"
582 " </CPU>\n");
583
584 /* *** stack */
585
586 if (ptib)
587 {
588 ulStackBase = (ULONG) ptib->tib_pstack;
589 ulStackLimit = (ULONG) ptib->tib_pstacklimit;
590 }
591 else
592 if (pContextRec->ContextFlags & CONTEXT_CONTROL)
593 {
594 /* first, try to find the end of the stack object */
595 ulCount = 0xFFFFFFFF;
596 arc = DosQueryMem ((void *) pContextRec->ctx_RegEbp,
597 &ulCount, &ulFlags);
598 if (arc == NO_ERROR)
599 {
600 ulStackLimit = pContextRec->ctx_RegEbp + ulCount;
601 /* next, try to find the start of the stack object */
602 ul = (pContextRec->ctx_RegEbp & 0xFFFFF000);
603 while (arc == NO_ERROR && !(ulFlags & PAG_BASE))
604 {
605 ulCount = 0x1000;
606 ul -= 0x1000;
607 arc = DosQueryMem ((void *) ul, &ulCount, &ulFlags);
608 }
609 if (arc == NO_ERROR)
610 ulStackBase = ul;
611 }
612 }
613
614 fprintf (file, " <Stack");
615 if (ulStackBase)
616 fprintf (file, " base=\"%08lX\"", ulStackBase);
617 if (ulStackLimit)
618 fprintf (file, " limit=\"%08lX\"", ulStackLimit);
619 fprintf (file, ">\n");
620
621 if (pContextRec->ContextFlags & CONTEXT_CONTROL)
622 qt_excWriteStackFrames (file, ulStackLimit, pContextRec);
623
624 fprintf (file, " </Stack>\n");
625 fprintf (file, " </Thread>\n");
626}
627
628#ifndef max
629#define max(a,b) (((a) > (b)) ? (a) : (b))
630#endif
631
632/** @internal
633 * Writes the module description string (if found) as the 'desciption'
634 * attribute. Returns TRUE if the description was found and written.
635 */
636static BOOL qt_excWriteModuleDesc (FILE *file, char *pszFileName)
637{
638 /* see OS2TK\h\exe.h, OS2TK\h\exe386.h and LXSPEC.INF for details */
639 enum { EXEID = 0x5a4d, LXOffset = 0x3C, sizeof_exe = LXOffset + 4,
640 E32MAGIC = 0x584c, e32_nrestab = 34 * 4,
641 sizeof_e32 = e32_nrestab + 4,
642 max_nresname = 255 + 1 /* trailing 0 */ };
643
644 UCHAR achBuf [max (max_nresname, max (sizeof_e32, sizeof_exe))];
645
646 BOOL bFound = FALSE;
647 ULONG ul1 = 0, ul2 = 0;
648
649 HFILE hf = NULL;
650 APIRET arc = NO_ERROR;
651
652 arc = DosOpen (pszFileName, &hf, &ul1,
653 0, 0, OPEN_ACTION_OPEN_IF_EXISTS,
654 OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY,
655 NULL);
656 if (arc != NO_ERROR)
657 return FALSE;
658
659 do
660 {
661 /* read the old EXE header plus the offset to the LX header */
662 arc = DosRead (hf, achBuf, sizeof_exe, &ul1);
663 if (arc != NO_ERROR || sizeof_exe != ul1)
664 break;
665
666 if (*(USHORT *) &achBuf == EXEID)
667 {
668 /* go to the LX header */
669 ul1 = *(ULONG *) &achBuf [LXOffset];
670 arc = DosSetFilePtr (hf, (LONG) ul1, FILE_BEGIN, &ul2);
671 if (arc != NO_ERROR || ul1 != ul2)
672 break;
673
674 /* read it upto the e32_nrestab field */
675 arc = DosRead (hf, achBuf, sizeof_e32, &ul1);
676 if (arc != NO_ERROR || sizeof_e32 != ul1 ||
677 *(USHORT *) &achBuf != E32MAGIC)
678 break;
679 }
680 else
681 if (*(USHORT *) &achBuf == E32MAGIC)
682 {
683 /* there may be no old EXE header, but LX only,
684 * read it upto the e32_nrestab field */
685 if (sizeof_e32 > sizeof_exe)
686 {
687 arc = DosRead (hf, achBuf + sizeof_exe,
688 sizeof_e32 - sizeof_exe, &ul1);
689 if (arc != NO_ERROR || sizeof_e32 - sizeof_exe != ul1)
690 break;
691 }
692 }
693 else
694 break;
695
696 /* go to the beginning of the non-resident name table */
697 ul1 = *(ULONG *) &achBuf [e32_nrestab];
698 if (ul1)
699 {
700 arc = DosSetFilePtr (hf, (LONG) ul1, FILE_BEGIN, &ul2);
701 if (arc != NO_ERROR || ul1 != ul2)
702 break;
703
704 /* read the first entry */
705 arc = DosRead (hf, achBuf, max_nresname, &ul1);
706 if (arc != NO_ERROR || max_nresname != ul1)
707 break;
708
709 /* if the ordinal is 0, then it's a description field (we don't do
710 * the full table search, since it shoud be ordered by ordinals) */
711 if (*(USHORT *) &achBuf [1 + *achBuf] == 0)
712 {
713 /* place the trailing zero */
714 achBuf [1 + *achBuf] = 0;
715 /* write the description attribute */
716 fprintf (file, " desc=\"%s\"", achBuf + 1);
717 bFound = TRUE;
718 }
719 }
720 }
721 while (0);
722
723 DosClose (hf);
724 return bFound;
725}
726
727/** \internal
728 * Writes module information to the log file.
729 * \a ulOrigin is 1 (self), 2 (imported), or 3 (loaded). Other values are
730 * ignored.
731 */
732static void qt_excWriteModule (FILE *file, USHORT hmte, char *pszName,
733 ULONG ulOrigin = 0)
734{
735 static const char *apszOrigins[] = { "self", "imported", "loaded" };
736
737 FILESTATUS3 Status;
738 APIRET arc = NO_ERROR;
739
740 fprintf (file, " <Module ID=%\"%04lX\" name=\"", hmte);
741 qt_excEscapeString (file, pszName);
742 fprintf (file, "\"");
743
744 arc = DosQueryPathInfo (pszName, FIL_STANDARD, &Status, sizeof (Status));
745
746 if ((ulOrigin >= 1 && ulOrigin <= 3) || arc == NO_ERROR)
747 {
748 if (ulOrigin >= 1 && ulOrigin <= 3)
749 fprintf (file, " origin=\"%s\"",
750 apszOrigins [ulOrigin - 1]);
751 if (arc == NO_ERROR)
752 fprintf (file, " timestamp=\"%04d-%02d-%02dT%02d:%02d:%02d\"",
753 Status.fdateLastWrite.year + 1980,
754 Status.fdateLastWrite.month, Status.fdateLastWrite.day,
755 Status.ftimeLastWrite.hours, Status.ftimeLastWrite.minutes,
756 Status.ftimeLastWrite.twosecs * 2);
757 }
758
759 qt_excWriteModuleDesc (file, pszName);
760
761 fprintf (file, "/>\n");
762}
763
764/** \internal
765 * Writes module and all its imports information to the log file.
766 * \a ulOrigin is 1 (self), or 3 (loaded), other values are ignored.
767 */
768static void qt_excWriteModules (FILE *file, USHORT hmte, QSLREC *pFirstLibRec,
769 ULONG ulOrigin)
770{
771 QSLREC *pLibRec = pFirstLibRec;
772 while (pLibRec)
773 {
774 if (pLibRec->hmte == hmte &&
775 pLibRec->pName /* not yet visited */)
776 {
777 qt_excWriteModule (file, hmte, (char *) pLibRec->pName, ulOrigin);
778 /* mark as visited */
779 pLibRec->pName = NULL;
780 /* go through imports */
781 if (pLibRec->ctImpMod)
782 {
783 USHORT *pHmte = (USHORT *) (pLibRec + 1);
784 for (ULONG i = 0; i < pLibRec->ctImpMod; ++ i, ++ pHmte)
785 qt_excWriteModules (file, *pHmte, pFirstLibRec,
786 ulOrigin == 1 ? 2 : ulOrigin);
787 break;
788 }
789 }
790 pLibRec = (QSLREC *) pLibRec->pNextRec;
791 }
792}
793
794/** @internal
795 * Struct for recursive qt_excWriteModulesOnStack() to reduce stack usage
796 */
797typedef struct _WMOS_STATE
798{
799 FILE *file;
800 ULONG ulRegEbp;
801 ULONG ulRegEip;
802 ULONG ulStackLimit;
803 USHORT hmteExe;
804 ULONG cLevel;
805 USHORT *pHmteOuter;
806} WMOS_STATE;
807
808/** \internal
809 * Walks the stack and writes information about all encountered modules.
810 * Used only when DosQuerySysState() fails (so qt_excWriteModules() is not
811 * applicable). We use recursiveness to avoid module record duplicates.
812 * @note This function should be in sync with qt_excWriteStackFrames()
813 * because they share the same logic.
814 */
815static void qt_excWriteModulesOnStack (WMOS_STATE *pState)
816{
817 USHORT hmteThis = 0;
818
819 {
820 APIRET arc = NO_ERROR;
821 char szMod [CCHMAXPATH] = "";
822 ULONG ulObject = 0, ulOffset = 0;
823
824 arc = DosQueryModFromEIP ((HMODULE *) &hmteThis, &ulObject,
825 sizeof (szMod), szMod, &ulOffset,
826 pState->ulRegEip);
827 if (arc == NO_ERROR && hmteThis != pState->hmteExe)
828 {
829 /* look if we've already seen this module using the hmteThis chain
830 * on the stack */
831 USHORT *pH = &hmteThis;
832 /* calculate distance between recursive hmteThis on the stack */
833 ULONG ulOuter = (ULONG) pState->pHmteOuter - (ULONG) pH;
834 ULONG cL = pState->cLevel;
835 while (cL != 0)
836 {
837 pH = (USHORT *)(((ULONG) pH) + ulOuter);
838 if (*pH == hmteThis)
839 break;
840 -- cL;
841 }
842 if (cL == 0)
843 {
844 /* got an unique module handle */
845 DosQueryModuleName (hmteThis, sizeof (szMod), szMod);
846 qt_excWriteModule (pState->file, hmteThis, szMod);
847 }
848 }
849 }
850
851 /* we do EBP validity check here as well as before the recursive
852 * call below to get a chance for the above code (EIP to hmod translation)
853 * to be executed even if EBP there is invalid. */
854
855 if (pState->ulRegEbp != 0 && pState->ulRegEbp < pState->ulStackLimit)
856 {
857 if ((pState->ulRegEbp & 0x00000FFF) == 0x00000000)
858 {
859 /* we're on a page boundary: check access */
860 while (pState->ulRegEbp < pState->ulStackLimit)
861 {
862 ULONG ulCount = 0x1000;
863 ULONG ulFlags = 0;
864 APIRET arc = DosQueryMem ((void *) pState->ulRegEbp,
865 &ulCount, &ulFlags);
866 if ( (arc != NO_ERROR)
867 || ( arc == NO_ERROR
868 && (ulFlags & (PAG_COMMIT | PAG_READ))
869 != (PAG_COMMIT | PAG_READ)))
870 {
871 /* try go to the next page */
872 /// @todo (r=dmik) I don't know how much is it accurate,
873 // I've just taken the logic from xwphelpers sources.
874 pState->ulRegEbp += 0x1000;
875 continue; /* while */
876 }
877 break;
878 }
879 }
880
881 /* get the return address of the current call */
882 pState->ulRegEip = *(((PULONG) pState->ulRegEbp) + 1);
883 /* get the address of the outer stack frame */
884 pState->ulRegEbp = *((PULONG) pState->ulRegEbp);
885
886 if (pState->ulRegEbp != 0 && pState->ulRegEbp < pState->ulStackLimit)
887 {
888 pState->cLevel ++;
889 pState->pHmteOuter = &hmteThis;
890 qt_excWriteModulesOnStack (pState);
891 }
892 }
893}
894
895/** @internal Simple process info struct */
896typedef struct _PROCESSINFO
897{
898 /* common values */
899 char *pszFullName;
900 char *pszBaseName;
901 /* direct info pointers */
902 PPIB ppib;
903 PTIB ptib;
904 BOOL bHaveSysState; /**< TRUE when both pProcRec and pLibRec are not NULL */
905 QSPREC *pProcRec; /**< NULL when bHaveSysState is FALSE */
906 QSLREC *pLibRec; /**< NULL when bHaveSysState is FALSE */
907} PROCESSINFO;
908
909/** \internal
910 * Writes exception information to the log file.
911 */
912static void qt_excWriteException (FILE *file,
913 PROCESSINFO *pInfo,
914 PEXCEPTIONREPORTRECORD pReportRec,
915 PCONTEXTRECORD pContextRec)
916{
917 ULONG ul = 0;
918 ULONG aulBuf [3];
919
920 QSTREC *pThrdRec = NULL;
921
922 /* *** application info */
923
924 {
925 fprintf (file, " <Application name=\"");
926 if (XcptPvt::askCallback (QtOS2SysXcptReq_AppName) == TRUE)
927 XcptPvt::letCallback (QtOS2SysXcptReq_AppName);
928 else
929 qt_excEscapeString (file, pInfo->pszBaseName);
930 fprintf (file, "\" version=\"");
931 if (XcptPvt::askCallback (QtOS2SysXcptReq_AppVer) == TRUE)
932 XcptPvt::letCallback (QtOS2SysXcptReq_AppVer);
933 else
934 fprintf (file, "unknown");
935 fprintf (file, "\">\n");
936
937 if (XcptPvt::askCallback (QtOS2SysXcptReq_ReportTo) == TRUE)
938 {
939 fprintf (file, " <Report to=\"");
940 XcptPvt::letCallback (QtOS2SysXcptReq_ReportTo);
941 if (XcptPvt::askCallback (QtOS2SysXcptReq_ReportSubj) == TRUE)
942 {
943 fprintf (file, "\" subject=\"");
944 XcptPvt::letCallback (QtOS2SysXcptReq_ReportSubj);
945 }
946 fprintf (file, "\"/>\n");
947 }
948
949 fprintf (file, " </Application>\n");
950 }
951
952 /* *** generic exception info */
953
954 fprintf (file,
955 " <Exception type=\"%08lX\" flags=\"%08lX\" address=\"%08lX\">\n",
956 pReportRec->ExceptionNum, pReportRec->fHandlerFlags,
957 (ULONG) pReportRec->ExceptionAddress);
958
959 for (ul = 0; ul < pReportRec->cParameters; ++ul)
960 {
961 fprintf (file, " <Param value=\"%08lX\"/>\n",
962 pReportRec->ExceptionInfo [ul]);
963 }
964
965 fprintf (file, " </Exception>\n");
966
967 /* *** system info */
968
969 DosQuerySysInfo (QSV_VERSION_MAJOR, QSV_VERSION_REVISION,
970 &aulBuf, sizeof (aulBuf));
971 /* Warp 3 is reported as 20.30 */
972 /* Warp 4 is reported as 20.40 */
973 /* Aurora is reported as 20.45 */
974
975 fprintf (file,
976 " <System name=\"OS/2\" version=\"%u.%u.%u\"/>\n",
977 aulBuf[0], aulBuf[1], aulBuf[2]);
978
979 /* *** process info */
980
981 fprintf (file, " <Process ID=\"%04lX\" parentID=\"%04lX\">\n",
982 pInfo->ppib->pib_ulpid, pInfo->ppib->pib_ulppid);
983
984 fprintf (file, " <Modules>\n");
985 if (pInfo->bHaveSysState)
986 {
987 /* write process module and it's imports */
988 qt_excWriteModules (file, pInfo->ppib->pib_hmte, pInfo->pLibRec, 1);
989 /* write loaded modules and their imports */
990 if (pInfo->pProcRec->cLib && pInfo->pProcRec->pLibRec)
991 {
992 for (ULONG i = 0; i < pInfo->pProcRec->cLib; ++ i)
993 qt_excWriteModules (file, pInfo->pProcRec->pLibRec [i],
994 pInfo->pLibRec, 3);
995 }
996 }
997 else
998 {
999 qt_excWriteModule (file, pInfo->ppib->pib_hmte, pInfo->pszFullName, 1);
1000 WMOS_STATE State = { file, pContextRec->ctx_RegEbp,
1001 pContextRec->ctx_RegEip,
1002 (ULONG) pInfo->ptib->tib_pstacklimit,
1003 pInfo->ppib->pib_hmte,
1004 0 /* cLevel */, NULL /* pHmteOuter */ };
1005 qt_excWriteModulesOnStack (&State);
1006 }
1007 fprintf (file, " </Modules>\n");
1008
1009 fprintf (file, " <Threads>\n");
1010
1011 /* first, the current thread */
1012 pThrdRec = pInfo->bHaveSysState ? pInfo->pProcRec->pThrdRec : NULL;
1013 if (pThrdRec)
1014 {
1015 /* locate the current thread structure */
1016 for (ul = 0; ul < pInfo->pProcRec->cTCB; ++ ul, ++ pThrdRec)
1017 if ((ULONG) pThrdRec->tid == pInfo->ptib->tib_ptib2->tib2_ultid)
1018 break;
1019 if (ul == pInfo->pProcRec->cTCB)
1020 pThrdRec = NULL;
1021 }
1022 qt_excWriteThreadInfo (file, pInfo->ptib, pThrdRec, pContextRec);
1023
1024 /* then, all other threads */
1025 pThrdRec = pInfo->bHaveSysState ? pInfo->pProcRec->pThrdRec : NULL;
1026 if (pThrdRec)
1027 {
1028 /* important to set ContextFlags to CONTEXT_FULL, otherwise we'll
1029 * get nothing */
1030 CONTEXTRECORD ContextRec = {CONTEXT_FULL, 0};
1031 APIRET arc = NO_ERROR;
1032 /* enter the critical section to effectively suspend all other threads
1033 * while reading their contexts and walking their stacks */
1034 DosEnterCritSec();
1035 for (ul = 0; ul < pInfo->pProcRec->cTCB; ++ ul, ++ pThrdRec)
1036 {
1037 if ((ULONG) pThrdRec->tid == pInfo->ptib->tib_ptib2->tib2_ultid)
1038 continue;
1039 arc = DosQueryThreadContext (pThrdRec->tid, CONTEXT_FULL,
1040 &ContextRec);
1041 if (arc == NO_ERROR)
1042 qt_excWriteThreadInfo (file, NULL, pThrdRec, &ContextRec);
1043 else
1044 {
1045 fprintf (file, " <Thread ID=\"%04lX\">\n", pThrdRec->tid);
1046 qt_excWriteErrorMsg (file, 3, "DosQueryThreadContext returned "
1047 "%lu", arc);
1048 fprintf (file, " </Thread>\n");
1049 }
1050 }
1051 /* exit the critical section */
1052 DosExitCritSec();
1053 }
1054
1055 fprintf (file, " </Threads>\n");
1056
1057 fprintf (file, " </Process>\n");
1058}
1059
1060/**
1061 * Opens an unique log file using \a pcszBasePath as the base path.
1062 * On input, \a pszFileName is the desired base file name w/o extension.
1063 * If it doesn't fit into the file name length limit (ENAMETOOLONG) for the
1064 * FS of the given disk, then a 8x3 file name is attemptet as a fallback.
1065 * On output, \a pszFileName will contain the full file name of the opened
1066 * log file. Note that \a pszFileName must be at least CCHMAXPATH bytes length.
1067 */
1068static FILE *qt_excOpenLogFile (const char *pcszBasePath, char *pszFileName)
1069{
1070 FILE *file = NULL;
1071
1072 char szFileName[CCHMAXPATH];
1073 char szDir[] = "\\SysTraps";
1074 char szFile[] = "\\12345678.123";
1075 enum { cbFileName = sizeof(szFileName) };
1076 enum { cbDir = sizeof(szDir) };
1077 enum { cbFile = sizeof(szFile) };
1078
1079 ULONG ul = 0;
1080 ULONG cbDirLen = 0;
1081 ULONG cbLen = 0;
1082 ULONG ulStamp = 0;
1083 char *pszStamp = NULL;
1084
1085 if (pcszBasePath == NULL || pszFileName == NULL)
1086 return NULL;
1087
1088 if (access (pcszBasePath, F_OK) != 0)
1089 return NULL;
1090
1091 if (strlen (pcszBasePath) + cbDir + cbFile - 2 >= CCHMAXPATH)
1092 return NULL;
1093
1094 /* get the full path if it's not 'X:' */
1095 if (strlen(pcszBasePath) != 2 ||
1096 pcszBasePath[1] != ':')
1097 {
1098 if (DosQueryPathInfo (pcszBasePath, FIL_QUERYFULLNAME,
1099 szFileName, cbFileName) != NO_ERROR)
1100 return NULL;
1101 }
1102 else
1103 strcpy (szFileName, pcszBasePath);
1104
1105 strcat (szFileName, szDir);
1106 if (access (szFileName, F_OK) != 0)
1107 if (mkdir (szFileName, 0777) != 0)
1108 return NULL;
1109
1110 cbDirLen = strlen (szFileName);
1111
1112 /* first, try to use the desired base file name */
1113
1114 /* we will append -NN to the file name to add some 'fraction of a
1115 * second' granularity to avoid name conflicts (0 <= NNN <= 99) */
1116 cbLen = strlen (pszFileName);
1117 if (cbDirLen + cbLen + 12 /* \-NN.trp.xml */ < CCHMAXPATH)
1118 {
1119 strcat (szFileName, "\\");
1120 strcat (szFileName, pszFileName);
1121 cbLen += cbDirLen + 1 /* \ */;
1122 for (ul = 0; ul < 100; ++ul)
1123 {
1124 sprintf (szFileName + cbLen, "-%02ld.trp.xml", ul);
1125 if (access (szFileName, F_OK) == 0)
1126 continue;
1127 file = fopen (szFileName, "wt");
1128 if (file)
1129 break;
1130 if (errno == ENAMETOOLONG)
1131 break;
1132 }
1133 }
1134
1135 /* next, try a time stamp as a 8x3 file name */
1136
1137 if (file == NULL)
1138 {
1139 pszStamp = szFileName + cbDirLen + 1;
1140 strcat (szFileName, szFile);
1141
1142 ulStamp = time (NULL);
1143
1144 /* In order to add some 'fraction of a second' granularity to the
1145 * timestamp, we shift it by 5 bits. This gives us 0x07FFFFFF seconds
1146 * which is a period of approx. 4,25 years. 5 bits in turn give us 32
1147 * fractions of a second. */
1148 ulStamp <<= 5;
1149
1150 /* try some few next stamps if the first one fails */
1151 for (ul = 0; ul < 32; ++ul, ++ulStamp)
1152 {
1153 sprintf (pszStamp, "%08lX.xml", ulStamp);
1154 if (access (szFileName, F_OK) == 0)
1155 continue;
1156 file = fopen (szFileName, "wt");
1157 if (file)
1158 break;
1159 }
1160 }
1161
1162 if (file)
1163 {
1164 strncpy (pszFileName, szFileName, CCHMAXPATH - 1);
1165 pszFileName[CCHMAXPATH - 1] = '\0';
1166 }
1167
1168 return file;
1169}
1170
1171/** \internal
1172 * Qt Exception handler.
1173 */
1174/* static */
1175ULONG APIENTRY
1176QtOS2SysXcptMainHandler::handler (PEXCEPTIONREPORTRECORD pReportRec,
1177 PEXCEPTIONREGISTRATIONRECORD pRegRec,
1178 PCONTEXTRECORD pContextRec,
1179 PVOID pv)
1180{
1181 PROCESSINFO Info = {0};
1182
1183 /* From the VAC++3 docs:
1184 * "The first thing an exception handler should do is check the
1185 * exception flags. If EH_EXIT_UNWIND is set, meaning
1186 * the thread is ending, the handler tells the operating system
1187 * to pass the exception to the next exception handler. It does the
1188 * same if the EH_UNWINDING flag is set, the flag that indicates
1189 * this exception handler is being removed.
1190 * The EH_NESTED_CALL flag indicates whether the exception
1191 * occurred within an exception handler. If the handler does
1192 * not check this flag, recursive exceptions could occur until
1193 * there is no stack remaining."
1194 *
1195 * So for all these conditions, we exit immediately.
1196 */
1197
1198 if (pReportRec->fHandlerFlags &
1199 (EH_EXIT_UNWIND | EH_UNWINDING | EH_NESTED_CALL))
1200 return XCPT_CONTINUE_SEARCH;
1201
1202 /* setup the process info structure */
1203 DosGetInfoBlocks (&Info.ptib, &Info.ppib);
1204
1205 switch (pReportRec->ExceptionNum)
1206 {
1207 case XCPT_ACCESS_VIOLATION:
1208 case XCPT_INTEGER_DIVIDE_BY_ZERO:
1209 case XCPT_ILLEGAL_INSTRUCTION:
1210 case XCPT_PRIVILEGED_INSTRUCTION:
1211 case XCPT_INVALID_LOCK_SEQUENCE:
1212 case XCPT_INTEGER_OVERFLOW:
1213 case XCPT_BREAKPOINT:
1214 {
1215 /* "real" exceptions: */
1216
1217 APIRET arc = NO_ERROR;
1218 PVOID pBuf = NULL;
1219
1220 char szFullExeName [CCHMAXPATH] = "";
1221 char szBaseExeName [CCHMAXPATH] = "";
1222
1223 TIB2 Tib2Orig;
1224 DATETIME dt;
1225
1226 char szFileName [CCHMAXPATH];
1227 FILE *file = NULL;
1228
1229 /* raise this thread's priority to do it fast */
1230 Tib2Orig = *Info.ptib->tib_ptib2;
1231 Info.ptib->tib_ptib2 = &Tib2Orig;
1232 DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0);
1233
1234 DosBeep (880, 200);
1235
1236 /* query system state */
1237 {
1238 ULONG ulCB = 64 * 1024;
1239 ULONG cTries = 3;
1240 while (-- cTries)
1241 {
1242 arc = DosAllocMem (&pBuf, ulCB, PAG_WRITE | PAG_COMMIT);
1243 if (arc == NO_ERROR)
1244 {
1245 memset (pBuf, 0, ulCB);
1246 arc = DosQuerySysState (QS_PROCESS | QS_MTE, 0,
1247 Info.ppib->pib_ulpid, 0,
1248 pBuf, ulCB);
1249 if (arc == NO_ERROR)
1250 {
1251 QSPTRREC *pPtrRec = (QSPTRREC *) pBuf;
1252 Info.bHaveSysState = TRUE;
1253 Info.pProcRec = pPtrRec->pProcRec;
1254 Info.pLibRec = pPtrRec->pLibRec;
1255 }
1256 else if (arc == ERROR_BUFFER_OVERFLOW)
1257 {
1258 /* retry with some bigger buffer size */
1259 DosFreeMem (pBuf);
1260 pBuf = NULL;
1261 ulCB *= 2;
1262 continue;
1263 }
1264 }
1265 break;
1266 }
1267 }
1268
1269 /* get the main module name */
1270 DosQueryModuleName (Info.ppib->pib_hmte,
1271 sizeof (szFullExeName), szFullExeName);
1272 {
1273 /* find the base name (w/o path and extension) */
1274 char *psz = strrchr (szFullExeName, '\\');
1275 if (psz)
1276 ++ psz;
1277 if (!psz)
1278 psz = szFullExeName;
1279 strcpy (szBaseExeName, psz);
1280 psz = strrchr (szBaseExeName, '.');
1281 if (psz)
1282 *psz = '\0';
1283 Info.pszFullName = szFullExeName;
1284 Info.pszBaseName = szBaseExeName;
1285 }
1286
1287 /* compose the desired base file name for the log file
1288 * (<datetime>-<exe>) */
1289 DosGetDateTime (&dt);
1290 sprintf (szFileName, "%04d%02d%02d%02d%02d%02d-%s",
1291 dt.year, dt.month, dt.day,
1292 dt.hours, dt.minutes, dt.seconds,
1293 szBaseExeName);
1294
1295 /* open the log file: */
1296
1297 /* first, try the home directory, then the root drive
1298 * (see QDir::homeDirPath()) */
1299 file = qt_excOpenLogFile (getenv ("HOME"), szFileName);
1300 if (file == NULL)
1301 {
1302 char *pszHomeDrive = getenv ("HOMEDRIVE");
1303 char *pszHomePath = getenv ("HOMEPATH");
1304 if (pszHomeDrive && pszHomePath &&
1305 strlen (pszHomeDrive) + strlen (pszHomePath) < CCHMAXPATH)
1306 {
1307 char szHomePath [CCHMAXPATH];
1308 strcpy (szHomePath, pszHomeDrive);
1309 strcat (szHomePath, pszHomePath);
1310 file = qt_excOpenLogFile (szHomePath, szFileName);
1311 }
1312 }
1313 if (file == NULL)
1314 {
1315 char szBootDrive[] = "\0:";
1316 ULONG ulBootDrive;
1317 DosQuerySysInfo (QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
1318 &ulBootDrive, sizeof(ulBootDrive));
1319 szBootDrive[0] = (char) ulBootDrive + 'A' - 1;
1320 file = qt_excOpenLogFile (szBootDrive, szFileName);
1321 }
1322
1323 if (file != NULL)
1324 {
1325 fprintf (file,
1326 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
1327 "<!--\n"
1328 " A fatal error has occured while running this application.\n"
1329 " Please contact the application vendor, describe what happened\n"
1330 " and send the contents of this file saved locally as\n"
1331 " '%s'.\n"
1332 "-->\n",
1333 szFileName);
1334
1335 fprintf (file,
1336 "<Trap timestamp=\"%04d-%02d-%02dT%02d:%02d:%02d\"" //%c%02d:%02d\""
1337 " version=\"1.0\"\n"
1338 " generator=\"Qt Library Version %s\"\n"
1339 " xmlns=\"http://db.hugaida.com/xml/os2/sys/Trap\">\n",
1340 dt.year, dt.month, dt.day,
1341 dt.hours, dt.minutes, dt.seconds,
1342 //(dt.timezone > 0 ? '-' : '+'),
1343 //abs (dt.timezone / 60),
1344 //abs (dt.timezone % 60),
1345 QT_VERSION_STR);
1346
1347 XcptPvt::file = file;
1348
1349 /* write trap information */
1350 qt_excWriteException (file, &Info, pReportRec, pContextRec);
1351
1352 XcptPvt::file = NULL;
1353
1354 fprintf (file, "</Trap>\n\n");
1355 fclose (file);
1356
1357 /* attempt to display the created file */
1358 {
1359 STARTDATA SData = {0};
1360 PID pid = 0;
1361 ULONG ulSessID = 0;
1362
1363 SData.Length = sizeof (STARTDATA);
1364 SData.PgmName = "E.EXE";
1365 SData.PgmInputs = szFileName;
1366
1367 DosStartSession (&SData, &ulSessID, &pid);
1368 }
1369 }
1370 else
1371 DosBeep (220, 200);
1372
1373 if (pBuf != NULL)
1374 DosFreeMem (pBuf);
1375
1376 /* reset old priority */
1377 DosSetPriority (PRTYS_THREAD, (Tib2Orig.tib2_ulpri & 0x0F00) >> 8,
1378 (UCHAR) Tib2Orig.tib2_ulpri,
1379 0);
1380 }
1381 break;
1382 }
1383
1384 /* we never handle the exception ourselves */
1385
1386 if (Info.ptib->tib_ptib2->tib2_ultid == 1 &&
1387 QtOS2SysXcptMainHandler::libcHandler != NULL)
1388 {
1389 /* we are on the main thread and were installed from qt_init() during
1390 * QApplication initialization using a hack; pass control back to the
1391 * LIBC exception handler */
1392 return QtOS2SysXcptMainHandler::libcHandler (pReportRec, pRegRec,
1393 pContextRec, pv);
1394 }
1395
1396 return XCPT_CONTINUE_SEARCH;
1397}
1398
1399#endif // !defined(QT_PM_NO_SYSEXCEPTIONS)
Note: See TracBrowser for help on using the repository browser.