Opened 18 years ago

Closed 17 years ago

#33 closed enhancement (fixed)

Add the OS/2 system exception handler to the Qt library

Reported by: dmik Owned by: dmik
Priority: normal Milestone: qt-os2-3.3.1-rc07
Component: kernel Version:
Severity: normal Keywords: exception handler
Cc:

Description

We want more information about system exceptions happening in the Qt library and in applications built on top of it: the standard OS/2 popup log mechanism, as well as the kLIBC 0.6 exception handler provide not enough details (for example they don't walk the stack frames). In addition, the latter prints the exception info to stderr only, which makes little sense for release versions of PM applications.

Change History (8)

comment:1 Changed 18 years ago by dmik

Status: newassigned

comment:2 Changed 18 years ago by dmik

I've added the experimential exception handler in changeset:136. Here are some details of the implementation.

  1. For the main thread, the exception handler is installed in qt_init() called once a QApplication instance is created, and uninstalled in qt_cleanup() when the instance is destroyed. For all other Qt threads (QThread subclasses), the exception handler is installed just before run() is executed and uninstalled afterwards.
  2. The exception handler is only installed if the LIBC library provides its own exception handler (using some sort of hack by directly modifying its exception record). It is known that the exception record in OS/2 can only be allocated on the stack, but since we cannot access the stack of the main() function from within QApplication (nor can we access the stack of the function inside LIBC that calls main()), this hack seems to be the only way to "correctly" install the exception handler for the main thread. Actually, I have very little knowledge about exception handling yet, so this approach should be considered experimential.
  3. Our exception handler is informative only, it doesn't perform any action other than collect the exception information and pass control back to the LIBC exception handler.
  4. The created trap file is an XML file that contains all usual system exception info plus the stack trace (I'll publish the XML schema definition for it later). This file is created in the %HOME% directory (or in the root directory of the boot drive if %HOME% is not defined) under the .qt3traps subdirectory (one file per every trap) The file name format is <datetime>-<exe_name>-<nn>.xml for filesystems supporting long file names, or just <timestamp>.xml for ones that don't.
  5. Qt applications may install the exception callback (see source:trunk/include/qwindowdefs_pm.h for definitions) that is used to obtain application-specific info to be included to the trap file. Currently, applications can tell their name, version and e-mail address to send trap files to. The idea is to write a special application that watches the trap files, displays them using some nice GUI when traps happens, and provides an easy interface to send them to the program author. Later, the amount of information provided by the application may be easily increased (for example, to include some special instructions on how to submit trap files, or additional information about the application state and configuration).
  6. For now, every time an exception happens, the exception handler starts E.EXE in a separate session to immediately display the contents of the trap file and grab user attention (looks not that bad, imho). Later, the Qt exception watcher is supposed to be executed instead of E.EXE.

The initial exception handler code is partly based on the except.h and except.c sources from the xwphelpers package (which is a part of the xworkplace product, see http://www.xworkplace.org, http://xworkplace.netlabs.org/ for more info). XWorkplace is Copyright (C) 1999-2002 Ulrich Moeller. Thanks, Ulrich.

comment:3 Changed 18 years ago by dmik

The next thing I'm going to do is to improve the exception handler so that it will print symbol information when walking the stack frames, using the debug info contained in executables/DLLs, or external .SYM files. The code will again be partly taken from the xwpheplers package (debug.h and debug.c). This will simplify debugging of hardly reproducible bugs a lot.

comment:4 Changed 18 years ago by dmik

Other ideas worth implementing are:

  1. Synchronize the exception handler to make it capable of processing two independent simultaneous exeptions on different threads.
  2. Grab contexts and do stack traces of all threads of the process, not only one that caught the real exception (depends on 1). The simplest solution seems to be to raise a user-defined system exception for that purpose (but maybe it's not the best).

comment:5 Changed 18 years ago by dmik

Forgot to mention that the Qt exception handler can be completely disabled by defining QT_PM_NO_SYSEXCEPTIONS when building Qt.

comment:6 Changed 18 years ago by dmik

Practice shows that a QApplication instance may be created not at the very beginning of the main() function but later, after calling some other (say, static methods) inside Qt. This leaves such code unprotected against system exceptions. Therefore, I decided to introduce a special stack-based class, QtOS2SysXcptMainHandler, that can be instantiated in main() prior to creating a QApplication instance or doing something else.

The constructor of this class does two things: a) installs the exception handler for the current thread and installs an optional exception callback function (so there is no need in a separate qInstallXcptCallback API). The destructor just uninstalls the exception handler if it was installed. There are no other methods in this class. Creating an instance of this class is only meaningful on the main thread (it does nothing otherwise).

The hackish way of attaching to the existing main thread's exception handler installed by LIBC is left as is in qt_init(), however. This is to ensure that our exception handler is automatically installed in all Qt-based programs (in all programs that use QApplication, to be exact) even if the programmer doesn't do it explicitly. When both QApplication and QtOS2SysXcptMainHandler are instantiated, the one that is first wins (and the other one doesn't touch the exception handing at all).

I also realized that the exception handler has nothing to do with PM, so I moved it from the kernel module to the tools module, which also makes it possible to use in console programs that dont use QApplication at all. Therefore, all definitions were moved from qwindowdefs_pm.h to qt_os2.h. Also, the QT_PM_NO_SYSEXCEPTIONS define was renamed to QT_OS2_NO_SYSEXCEPTIONS (to reflect its non-PM nature).

comment:7 Changed 18 years ago by dmik

Implemented some more functionality, in particular: querying and writing the list of all imported/loaded modules (using the DosQuerySysState?() API) and grabbing the context and the call stack of all other process threads (besides the one that got an exception). Works not that bad.

The next step is to replace fprintf() and friends with the direct usage of DosWrite?() and friends, for more safety (LIBC may be unstable or even screwed up when an exception happens).

comment:8 Changed 17 years ago by dmik

Resolution: fixed
Status: assignedclosed

I think it's enough with the exception handler features for now, it's already quite powerful. I'm closing the ticket at changeset:151.

I decided not to convert fprintf() to DosWrite?() for now, because this exception handler is intended to catch problems in Qt and Qt-based applications in the first place (i.e. not in kLIBC itself). However, I've also got plans to create a patch for kLIBC one day that will move the implemented functionality to the kLIBC's exception handler (so that trap reports will be generated for *all* applications using kLIBC, not only Qt). That day will be the best time to do such a convertion (and more over, it's a kind of requirement, because fprintf() is itself a part of kLIBC so we can't rely on it any more if we handle exceptions there). Knut doesn't seem to mind.

Note: See TracTickets for help on using tickets.