I have just committed an interesting change to Qt 4.4 now, fixing an open task reported by David Faure. By itself, the change is hardly worth mentioning:
-#if (defined(Q_OS_UNIX) || defined(Q_CC_MINGW)) && defined(QT_DEBUG)
+#if (defined(Q_OS_UNIX) || defined(Q_CC_MINGW))
abort(); // trap; generates core dump
#else
exit(1); // goodbye cruel world
The interesting part is the story behind the change.
Last night, some KDE friends (Dirk and David to be precise) pointed out that Q_ASSERT and qFatal only call abort(2) if Qt was compiled in debug mode, thereby making it impossible to use a crash handler to display a message to the user, maybe to restart the application as well. You could argue that an application shipped to a user should not have assertions enabled and should not be tripping them anyways, even if you had them enabled. And you’d be correct. The issue was that the application in question was still in development phase, in debug mode, therefore with assertions enabled. But Qt was in release mode, so it simply called exit(3) instead of aborting. The result is that the application being debugged and developed simply disappears, leaving no trace behind of why.
So they asked me: why was that the code like that in the first place?
Inspired by a recent movie I watched in the cinema, that set me off in an archeology expedition, digging through the history of Qt code — more precisely, the qglobal.cpp file. After a few minutes, I had managed to trace down the actual change, after going through two renames of the file (from src/tools/qglobal.cpp [Qt 1 to 3 forms] to src/core/global/qglobal.cpp [unreleased Qt 4 form] to src/corelib/global/qglobal.cpp [current form]), one wide-reaching whitespace change, removing all Tabs in Qt source code, and several renames of the macros, to this change:
Author: Haavard Nord
Date: Tue Apr 18 15:43:46 1995 +0100fatal() calls abort() if debug flag defined
-#if defined(UNIX)
+#if defined(UNIX) && defined(DEBUG)
abort(); // trap; generates core dump
#else
exit( 1 ); // goodbye cruel world
That means I have just reverted a 13-year-old commit by one of the Trolltech founders!
If we dig further, to the history of the abort() line itself, we end up in change number 43, whose log message is:
Author: Haavard Nord
Date: Mon Sep 5 05:54:23 1994 +0100Initial revision
And I can’t go beyond that… As with many projects, Qt’s first authors decision to use a version control system was an afterthought. That change above added 43 files and 8438 lines of code.
So, here’s what the qFatal function looked like in Sep 5, 1994:
void fatal( const char *msg, ... ) // print message and exit
{
char buf[240];
va_list ap;
va_start( ap, msg ); // use variable arg list
if ( handler ) {
vsprintf( buf, msg, ap );
(*handler)( buf );
}
else {
vfprintf( stderr, msg, ap );
fprintf( stderr, "n" ); // add newline
}
va_end( ap );
#if defined(UNIX)
abort(); // trap; generates core dump
#else
exit( 1 ); // goodbye cruel world
#endif
}
And here’s what the equivalent code looks like today, June 5th, 2008, in what will be released as Qt 4.4.1 (edited for brevity):
void qFatal(const char *msg, ...)
{
char buf[QT_BUFFER_LENGTH];
buf[QT_BUFFER_LENGTH - 1] = ' ';
va_list ap;
va_start(ap, msg); // use variable arg list
if (msg)
qvsnprintf(buf, QT_BUFFER_LENGTH - 1, msg, ap);
va_end(ap);
qt_message_output(QtFatalMsg, buf);
}
void qt_message_output(QtMsgType msgType, const char *buf)
{
if (handler) {
(*handler)(msgType, buf);
} else {
[...]
fprintf(stderr, "%sn", buf);
fflush(stderr);
}
if (msgType == QtFatalMsg
|| (msgType == QtWarningMsg
&& (!qgetenv("QT_FATAL_WARNINGS").isNull())) ) {
[...]
#if (defined(Q_OS_UNIX) || defined(Q_CC_MINGW))
abort(); // trap; generates core dump
#else
exit(1); // goodbye cruel world
#endif
}
}
7 Responses to “Restoring original Qt behaviour”
Thanks for blogging this. I’ve stumbled upon some bug(s) in Amarok and/or phonon_qt7 in OS X that are causing crashes w/ no drkonqui or os x crash handler being called. I’m hoping I can apply this patch myself and get a bactrace going without gdb. Hmm, maybe i’ll just use gdb directly…
What about Windows? Why isn’t abort also called for MS Windows platforms? On Windows you can trap the sigabrt and use the built-in or external crash reporter.
@WhatAboutWindows: er, there is no such thing as sigabrt on Windows.
I was in on that discussion
Anyways, I’m glad that this finally got changed. I find it most convenient to develop with the libraries built in release with debug symbols (that’s the default configuration too!) and have always worked around this by just setting a breakpoint on exit(3) to see the final stack trace.
Thanks for this change! It annoyed me too, and I’ve worked around it by using qInstallMessageHandler().
abort and sigabrt exist on windows - Go read the MSDN documentation: http://msdn.microsoft.com/en-us/library/k089yyh0(VS.80).aspx
abort and sigbart exist on windows - read the MSDN documentation: http://msdn.microsoft.com/en-us/library/k089yyh0(VS.80).aspx