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

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

Common: System Exceptions:

  • Updated all comment and open/close parenthesis styles.
  • Changed the name of the trap file subdirectory to '.systraps' (preparing to create a LIBC patch); the full trap file name version now ends with '.trp.xml' (for easier file association setup).
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 40.6 KB
Line 
1/****************************************************************************
2** $Id: qsysxcpt_pm.cpp 149 2006-11-01 18:18:21Z 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[] = "\\.systrap";
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 + 12 /* \-NN.trp.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.trp.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
939 if (file == NULL)
940 {
941 pszStamp = szFileName + cbDirLen + 1;
942 strcat (szFileName, szFile);
943
944 ulStamp = time (NULL);
945
946 /* In order to add some 'fraction of a second' granularity to the
947 * timestamp, we shift it by 5 bits. This gives us 0x07FFFFFF seconds
948 * which is a period of approx. 4,25 years. 5 bits in turn give us 32
949 * fractions of a second. */
950 ulStamp <<= 5;
951
952 /* try some few next stamps if the first one fails */
953 for (ul = 0; ul < 32; ++ul, ++ulStamp)
954 {
955 sprintf (pszStamp, "%08lX.xml", ulStamp);
956 if (access (szFileName, F_OK) == 0)
957 continue;
958 file = fopen (szFileName, "wt");
959 if (file)
960 break;
961 }
962 }
963
964 if (file)
965 {
966 strncpy (pszFileName, szFileName, CCHMAXPATH - 1);
967 pszFileName[CCHMAXPATH - 1] = '\0';
968 }
969
970 return file;
971}
972
973/** \internal
974 * Qt Exception handler.
975 */
976/* static */
977ULONG APIENTRY
978QtOS2SysXcptMainHandler::handler (PEXCEPTIONREPORTRECORD pReportRec,
979 PEXCEPTIONREGISTRATIONRECORD pRegRec,
980 PCONTEXTRECORD pContextRec,
981 PVOID pv)
982{
983 PROCESSINFO Info = {0};
984
985 /* From the VAC++3 docs:
986 * "The first thing an exception handler should do is check the
987 * exception flags. If EH_EXIT_UNWIND is set, meaning
988 * the thread is ending, the handler tells the operating system
989 * to pass the exception to the next exception handler. It does the
990 * same if the EH_UNWINDING flag is set, the flag that indicates
991 * this exception handler is being removed.
992 * The EH_NESTED_CALL flag indicates whether the exception
993 * occurred within an exception handler. If the handler does
994 * not check this flag, recursive exceptions could occur until
995 * there is no stack remaining."
996 *
997 * So for all these conditions, we exit immediately.
998 */
999
1000 if (pReportRec->fHandlerFlags &
1001 (EH_EXIT_UNWIND | EH_UNWINDING | EH_NESTED_CALL))
1002 return XCPT_CONTINUE_SEARCH;
1003
1004 /* setup the process info structure */
1005 DosGetInfoBlocks (&Info.ptib, &Info.ppib);
1006
1007 switch (pReportRec->ExceptionNum)
1008 {
1009 case XCPT_ACCESS_VIOLATION:
1010 case XCPT_INTEGER_DIVIDE_BY_ZERO:
1011 case XCPT_ILLEGAL_INSTRUCTION:
1012 case XCPT_PRIVILEGED_INSTRUCTION:
1013 case XCPT_INVALID_LOCK_SEQUENCE:
1014 case XCPT_INTEGER_OVERFLOW:
1015 {
1016 /* "real" exceptions: */
1017
1018 APIRET arc = NO_ERROR;
1019 PVOID pBuf = NULL;
1020
1021 char szFullExeName [CCHMAXPATH] = "";
1022 char szBaseExeName [CCHMAXPATH] = "";
1023
1024 TIB2 Tib2Orig;
1025 DATETIME dt;
1026
1027 char szFileName [CCHMAXPATH];
1028 FILE *file = NULL;
1029
1030 /* raise this thread's priority to do it fast */
1031 Tib2Orig = *Info.ptib->tib_ptib2;
1032 Info.ptib->tib_ptib2 = &Tib2Orig;
1033 DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0);
1034
1035 DosBeep (880, 200);
1036
1037 /* query system state */
1038 {
1039 ULONG ulCB = 64 * 1024;
1040 ULONG cTries = 3;
1041 while (-- cTries)
1042 {
1043 arc = DosAllocMem (&pBuf, ulCB, PAG_WRITE | PAG_COMMIT);
1044 if (arc == NO_ERROR)
1045 {
1046 memset (pBuf, 0, ulCB);
1047 arc = DosQuerySysState (QS_PROCESS | QS_MTE, 0,
1048 Info.ppib->pib_ulpid, 0,
1049 pBuf, ulCB);
1050 if (arc == NO_ERROR)
1051 {
1052 QSPTRREC *pPtrRec = (QSPTRREC *) pBuf;
1053 Info.bHaveSysState = TRUE;
1054 Info.pProcRec = pPtrRec->pProcRec;
1055 Info.pLibRec = pPtrRec->pLibRec;
1056 }
1057 else if (arc == ERROR_BUFFER_OVERFLOW)
1058 {
1059 /* retry with some bigger buffer size */
1060 DosFreeMem (pBuf);
1061 pBuf = NULL;
1062 ulCB *= 2;
1063 continue;
1064 }
1065 }
1066 break;
1067 }
1068 }
1069
1070 /* get the main module name */
1071 DosQueryModuleName (Info.ppib->pib_hmte,
1072 sizeof (szFullExeName), szFullExeName);
1073 {
1074 /* find the base name (w/o path and extension) */
1075 char *psz = strrchr (szFullExeName, '\\');
1076 if (psz)
1077 ++ psz;
1078 if (!psz)
1079 psz = szFullExeName;
1080 strcpy (szBaseExeName, psz);
1081 psz = strrchr (szBaseExeName, '.');
1082 if (psz)
1083 *psz = '\0';
1084 Info.pszFullName = szFullExeName;
1085 Info.pszBaseName = szBaseExeName;
1086 }
1087
1088 /* compose the desired base file name for the log file
1089 * (<datetime>-<exe>) */
1090 DosGetDateTime (&dt);
1091 sprintf (szFileName, "%04d%02d%02d%02d%02d%02d-%s",
1092 dt.year, dt.month, dt.day,
1093 dt.hours, dt.minutes, dt.seconds,
1094 szBaseExeName);
1095
1096 /* open the log file: */
1097
1098 /* first, try the home directory, then the root drive
1099 * (see QDir::homeDirPath()) */
1100 file = qt_excOpenLogFile (getenv ("HOME"), szFileName);
1101 if (file == NULL)
1102 {
1103 char *pszHomeDrive = getenv ("HOMEDRIVE");
1104 char *pszHomePath = getenv ("HOMEPATH");
1105 if (pszHomeDrive && pszHomePath &&
1106 strlen (pszHomeDrive) + strlen (pszHomePath) < CCHMAXPATH)
1107 {
1108 char szHomePath [CCHMAXPATH];
1109 strcpy (szHomePath, pszHomeDrive);
1110 strcat (szHomePath, pszHomePath);
1111 file = qt_excOpenLogFile (szHomePath, szFileName);
1112 }
1113 }
1114 if (file == NULL)
1115 {
1116 char szBootDrive[] = "\0:";
1117 ULONG ulBootDrive;
1118 DosQuerySysInfo (QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
1119 &ulBootDrive, sizeof(ulBootDrive));
1120 szBootDrive[0] = (char) ulBootDrive + 'A' - 1;
1121 file = qt_excOpenLogFile (szBootDrive, szFileName);
1122 }
1123
1124 if (file != NULL)
1125 {
1126 fprintf (file,
1127 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
1128 "<!--\n"
1129 " A fatal error has occured while running this application.\n"
1130 " Please contact the application vendor, describe what happened\n"
1131 " and send the contents of this file saved locally as\n"
1132 " '%s'.\n"
1133 "-->\n",
1134 szFileName);
1135
1136 fprintf (file,
1137 "<Trap timestamp=\"%04d-%02d-%02dT%02d:%02d:%02d\"" //%c%02d:%02d\""
1138 " version=\"1.0\"\n"
1139 " generator=\"Qt Library Version %s\"\n"
1140 " xmlns=\"http://db.hugaida.com/xml/os2/sys/Trap\">\n",
1141 dt.year, dt.month, dt.day,
1142 dt.hours, dt.minutes, dt.seconds,
1143 //(dt.timezone > 0 ? '-' : '+'),
1144 //abs (dt.timezone / 60),
1145 //abs (dt.timezone % 60),
1146 QT_VERSION_STR);
1147
1148 XcptPvt::file = file;
1149
1150 /* write trap information */
1151 qt_excWriteException (file, &Info, pReportRec, pContextRec);
1152
1153 XcptPvt::file = NULL;
1154
1155 fprintf (file, "</Trap>\n\n");
1156 fclose (file);
1157
1158 /* attempt to display the created file */
1159 {
1160 STARTDATA SData = {0};
1161 PID pid = 0;
1162 ULONG ulSessID = 0;
1163
1164 SData.Length = sizeof (STARTDATA);
1165 SData.PgmName = "E.EXE";
1166 SData.PgmInputs = szFileName;
1167
1168 DosStartSession (&SData, &ulSessID, &pid);
1169 }
1170 }
1171 else
1172 DosBeep (220, 200);
1173
1174 if (pBuf != NULL)
1175 DosFreeMem (pBuf);
1176
1177 /* reset old priority */
1178 DosSetPriority (PRTYS_THREAD, (Tib2Orig.tib2_ulpri & 0x0F00) >> 8,
1179 (UCHAR) Tib2Orig.tib2_ulpri,
1180 0);
1181 }
1182 break;
1183 }
1184
1185 /* we never handle the exception ourselves */
1186
1187 if (Info.ptib->tib_ptib2->tib2_ultid == 1 &&
1188 QtOS2SysXcptMainHandler::libcHandler != NULL)
1189 {
1190 /* we are on the main thread and were installed from qt_init() during
1191 * QApplication initialization using a hack; pass control back to the
1192 * LIBC exception handler */
1193 return QtOS2SysXcptMainHandler::libcHandler (pReportRec, pRegRec,
1194 pContextRec, pv);
1195 }
1196
1197 return XCPT_CONTINUE_SEARCH;
1198}
1199
1200#endif // !defined(QT_PM_NO_SYSEXCEPTIONS)
Note: See TracBrowser for help on using the repository browser.