# HG changeset patch # User Santeri Piippo # Date 1382122329 -10800 # Node ID 29eb671b34f6ff0fc09e3e30505adb885bafea34 # Parent adab82ab13a55f1ec84a1474521aee96bd53dc3d Added a crash catcher which trigger under Linux. It calls GDB and tries to get a backtrace. Also integrated assertion failure handling to this new system. Removed the print() function in the process (because the new bomb box uses a text edit and QTextDocuments's print method clashes with the macro and I figured it was a good idea to rid it anyway) and replaced all calls with log(). diff -r adab82ab13a5 -r 29eb671b34f6 ldforge.pro --- a/ldforge.pro Fri Oct 18 18:16:54 2013 +0300 +++ b/ldforge.pro Fri Oct 18 21:52:09 2013 +0300 @@ -24,9 +24,11 @@ CONFIG += debug_and_release CONFIG (debug, debug|release) { - TARGET = ldforge_debug + TARGET = ldforge_debug + DEFINES += DEBUG } else { TARGET = ldforge + DEFINES += RELEASE } unix { diff -r adab82ab13a5 -r 29eb671b34f6 src/colors.cpp --- a/src/colors.cpp Fri Oct 18 18:16:54 2013 +0300 +++ b/src/colors.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -35,19 +35,16 @@ // ----------------------------------------------------------------------------- void initColors() { LDColor* col; - print ("%1: initializing color information.\n", __func__); + log ("%1: initializing color information.\n", __func__); // Always make sure there's 16 and 24 available. They're special like that. col = new LDColor; - col->faceColor = - col->hexcode = "#AAAAAA"; + col->faceColor = col->hexcode = "#AAAAAA"; col->edgeColor = Qt::black; g_LDColors[maincolor] = col; col = new LDColor; - col->faceColor = - col->edgeColor = - col->hexcode = "#000000"; + col->faceColor = col->edgeColor = col->hexcode = "#000000"; g_LDColors[edgecolor] = col; parseLDConfig(); diff -r adab82ab13a5 -r 29eb671b34f6 src/common.h --- a/src/common.h Fri Oct 18 18:16:54 2013 +0300 +++ b/src/common.h Fri Oct 18 21:52:09 2013 +0300 @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -75,10 +74,6 @@ # define DIRSLASH_CHAR '/' #endif // WIN32 -#ifdef RELEASE -# define NDEBUG // remove asserts -#endif // RELEASE - #define PROP_NAME(GET) m_##GET #define READ_ACCESSOR(T, GET) \ @@ -144,12 +139,17 @@ // Replace assert with a version that shows a GUI dialog if possible. // On Windows I just can't get the actual error messages otherwise. +void assertionFailure (const char* file, int line, const char* funcname, const char* expr); + #ifdef assert -#undef assert +# undef assert #endif // assert -void assertionFailure (const char* file, int line, const char* funcname, const char* expr); -#define assert(N) ((N) ? (void) 0 : assertionFailure (__FILE__, __LINE__, FUNCNAME, #N)) +#ifdef DEBUG +# define assert(N) ((N) ? (void) 0 : assertionFailure (__FILE__, __LINE__, FUNCNAME, #N)) +#else +# define assert(N) +#endif // DEBUG // Version string identifier QString versionString(); diff -r adab82ab13a5 -r 29eb671b34f6 src/config.cpp --- a/src/config.cpp Fri Oct 18 18:16:54 2013 +0300 +++ b/src/config.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -61,7 +61,7 @@ // ----------------------------------------------------------------------------- bool Config::load() { QSettings* settings = getSettingsObject(); - print ("config::load: Loading configuration file from %1\n", settings->fileName()); + log ("config::load: Loading configuration file from %1\n", settings->fileName()); for (Config* cfg : g_configPointers) { if (!cfg) @@ -80,7 +80,7 @@ // ----------------------------------------------------------------------------- bool Config::save() { QSettings* settings = getSettingsObject(); - print ("Saving configuration to %1...\n", settings->fileName()); + log ("Saving configuration to %1...\n", settings->fileName()); for (Config* cfg : g_configPointers) { if (!cfg) diff -r adab82ab13a5 -r 29eb671b34f6 src/crashcatcher.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crashcatcher.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -0,0 +1,113 @@ +#ifdef __unix__ + +#include +#include +#include +#include +#include +#include +#include +#include "crashcatcher.h" +#include "types.h" +#include "dialogs.h" + +// Is the crash catcher active now? +static bool g_crashCatcherActive = false; + +// If an assertion failed, what was it? +static str g_assertionFailure; + +// List of signals to catch and crash on +static QList g_signalsToCatch ({ + SIGSEGV, // segmentation fault + SIGABRT, // abort() calls + SIGFPE, // floating point exceptions (e.g. division by zero) + SIGILL, // illegal instructions +}); + +// ============================================================================= +// ----------------------------------------------------------------------------- +static void handleSignal (int sig) +{ printf ("caught signal %d\n", sig); + + if (g_crashCatcherActive) + { printf ("caught signal while crash catcher is active!\n"); + return; + } + + if (g_signalsToCatch.indexOf (sig) != -1) + { const pid_t pid = getpid(); + QProcess proc; + QTemporaryFile commandsFile; + + g_crashCatcherActive = true; + printf ("%s: crashed with signal %d, launching gdb\n", __func__, sig); + + if (commandsFile.open()) + { commandsFile.write (fmt ("attach %1\n", pid).toLocal8Bit()); + commandsFile.write (str ("backtrace full\n").toLocal8Bit()); + commandsFile.write (str ("detach\n").toLocal8Bit()); + commandsFile.write (str ("quit").toLocal8Bit()); + commandsFile.flush(); + commandsFile.close(); + } + + QStringList args ({"-x", commandsFile.fileName()}); + + proc.start ("gdb", args); + + // Linux doesn't allow ptrace to be used on anything but direct child processes + // so we need to use prctl to register an exception to this to allow GDB attach to us. + // We need to do this now and no earlier because only now we actually know GDB's PID. + prctl (PR_SET_PTRACER, proc.pid(), 0, 0, 0); + + proc.waitForFinished (5000); + str output = QString (proc.readAllStandardOutput()); + str err = QString (proc.readAllStandardError()); + + bombBox (fmt ("

Program crashed with signal %1

\n\n" + "%2" + "

GDB stdout:

%3
\n" + "

GDB stderr:

%4
", + sig, (!g_assertionFailure.isEmpty()) ? g_assertionFailure : "", output, err)); + exit (137); + } +} + +// ============================================================================= +// ----------------------------------------------------------------------------- +void initCrashCatcher() +{ struct sigaction sighandler; + sighandler.sa_handler = &handleSignal; + sighandler.sa_flags = 0; + sigemptyset (&sighandler.sa_mask); + + for (int sig : g_signalsToCatch) + sigaction (sig, &sighandler, null); + + log ("%1: crash catcher hooked to signals: %2\n", __func__, g_signalsToCatch); +} +#endif // #ifdef __unix__ + +// ============================================================================= +// This function must be readily available in both Windows and Linux. We display +// the bomb box straight in Windows while in Linux we let abort() trigger the +// signal handler, which will cause the usual bomb box with GDB diagnostics. +// Said prompt will embed the assertion failure information. +// ----------------------------------------------------------------------------- +void assertionFailure (const char* file, int line, const char* funcname, const char* expr) +{ str errmsg = fmt ( + "

File: %1
" + "Line: %2
" + "Function: %3

" + "

Assertion `%4' failed.

", + file, line, funcname, expr); + + g_assertionFailure = errmsg; + +#ifndef __unix__ + bombBox (errmsg); +#endif + + abort(); +} \ No newline at end of file diff -r adab82ab13a5 -r 29eb671b34f6 src/crashcatcher.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crashcatcher.h Fri Oct 18 21:52:09 2013 +0300 @@ -0,0 +1,11 @@ +#ifndef LDFORGE_CRASHCATCHER_H +#define LDFORGE_CRASHCATCHER_H + +#ifdef __unix__ + +void initCrashCatcher(); + +#else // ifdef __unix__ +#define initCrashCatcher() +#endif // ifdef __unix__ +#endif // ifndef LDFORGE_CRASHCATCHER_H \ No newline at end of file diff -r adab82ab13a5 -r 29eb671b34f6 src/dialogs.cpp --- a/src/dialogs.cpp Fri Oct 18 18:16:54 2013 +0300 +++ b/src/dialogs.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -28,8 +28,8 @@ #include #include #include +#include #include - #include "dialogs.h" #include "widgets.h" #include "gui.h" @@ -42,6 +42,8 @@ #include "ui_openprogress.h" #include "ui_extprogpath.h" #include "ui_about.h" +#include "ui_bombbox.h" +#include "moc_dialogs.cpp" extern const char* g_extProgPathFilter; extern_cfg (String, io_ldpath); @@ -332,4 +334,15 @@ void AboutDialog::slot_mail() { QDesktopServices::openUrl (QUrl ("mailto:Santeri Piippo ?subject=LDForge")); } -#include "moc_dialogs.cpp" + +// ============================================================================= +// ----------------------------------------------------------------------------- +void bombBox (const str& message) +{ QDialog dlg (g_win); + Ui_BombBox ui; + + ui.setupUi (&dlg); + ui.m_text->setText (message); + ui.buttonBox->button (QDialogButtonBox::Close)->setText (QObject::tr ("Damn it")); + dlg.exec(); +} diff -r adab82ab13a5 -r 29eb671b34f6 src/dialogs.h --- a/src/dialogs.h Fri Oct 18 18:16:54 2013 +0300 +++ b/src/dialogs.h Fri Oct 18 21:52:09 2013 +0300 @@ -135,4 +135,6 @@ void slot_mail(); }; +void bombBox (const str& message); + #endif // LDFORGE_DIALOGS_H diff -r adab82ab13a5 -r 29eb671b34f6 src/download.cpp --- a/src/download.cpp Fri Oct 18 18:16:54 2013 +0300 +++ b/src/download.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -41,6 +41,7 @@ // ----------------------------------------------------------------------------- void PartDownloader::k_download() { str path = getDownloadPath(); + assert (false); if (path == "" || QDir (path).exists() == false) { critical (PartDownloader::tr ("You need to specify a valid path for " @@ -232,7 +233,7 @@ return; modifyDest (dest); - print ("DOWNLOAD: %1 -> %2\n", url, PartDownloader::getDownloadPath() + DIRSLASH + dest); + log ("DOWNLOAD: %1 -> %2\n", url, PartDownloader::getDownloadPath() + DIRSLASH + dest); PartDownloadRequest* req = new PartDownloadRequest (url, dest, primary, this); m_filesToDownload << dest; @@ -320,7 +321,7 @@ QDir dir (dirpath); if (!dir.exists()) - { print ("Creating %1...\n", dirpath); + { log ("Creating %1...\n", dirpath); if (!dir.mkpath (dirpath)) critical (fmt (tr ("Couldn't create the directory %1!"), dirpath)); diff -r adab82ab13a5 -r 29eb671b34f6 src/extprogs.cpp --- a/src/extprogs.cpp Fri Oct 18 18:16:54 2013 +0300 +++ b/src/extprogs.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -216,7 +216,7 @@ #endif // _WIN32 - print ("cmdline: %1 %2\n", path, argv.join (" ")); + log ("cmdline: %1 %2\n", path, argv.join (" ")); // Temporary files for stdin and stdout if (!mkTempFile (input, inputname) || !mkTempFile (output, outputname)) @@ -238,7 +238,7 @@ proc.waitForFinished(); #ifndef RELEASE - print ("%1", str (proc.readAllStandardOutput())); + log ("%1", str (proc.readAllStandardOutput())); #endif // RELEASE str err = ""; diff -r adab82ab13a5 -r 29eb671b34f6 src/file.cpp --- a/src/file.cpp Fri Oct 18 18:16:54 2013 +0300 +++ b/src/file.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -199,7 +199,7 @@ // ============================================================================= // ----------------------------------------------------------------------------- File* openLDrawFile (str relpath, bool subdirs) -{ print ("%1: Try to open %2\n", __func__, relpath); +{ log ("%1: Try to open %2\n", __func__, relpath); File* f = new File; str fullPath; @@ -241,7 +241,7 @@ } // Did not find the file. - print ("could not find %1\n", relpath); + log ("could not find %1\n", relpath); delete f; return null; } @@ -1147,7 +1147,7 @@ // ============================================================================= // ----------------------------------------------------------------------------- void loadLogoedStuds() -{ print ("Loading logoed studs...\n"); +{ log ("Loading logoed studs...\n"); delete g_logoedStud; delete g_logoedStud2; diff -r adab82ab13a5 -r 29eb671b34f6 src/gldraw.cpp --- a/src/gldraw.cpp Fri Oct 18 18:16:54 2013 +0300 +++ b/src/gldraw.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -302,7 +302,7 @@ if (obj->color() == i) return; - print ("%1: Unknown color %2!\n", __func__, obj->color()); + log ("%1: Unknown color %2!\n", __func__, obj->color()); g_warnedColors << obj->color(); return; } diff -r adab82ab13a5 -r 29eb671b34f6 src/main.cpp --- a/src/main.cpp Fri Oct 18 18:16:54 2013 +0300 +++ b/src/main.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -30,6 +30,8 @@ #include "primitives.h" #include "gldraw.h" #include "configDialog.h" +#include "dialogs.h" +#include "crashcatcher.h" QList g_loadedFiles; ForgeWindow* g_win = null; @@ -49,18 +51,19 @@ { QApplication app (argc, argv); app.setOrganizationName (APPNAME); app.setApplicationName (APPNAME); + g_app = &app; - g_app = &app; + initCrashCatcher(); LDFile::setCurrent (null); // Load or create the configuration if (!Config::load()) - { print ("Creating configuration file...\n"); + { log ("Creating configuration file...\n"); if (Config::save()) - print ("Configuration file successfully created.\n"); + log ("Configuration file successfully created.\n"); else - print ("failed to create configuration file!\n"); + log ("failed to create configuration file!\n"); } LDPaths::initPaths(); @@ -135,38 +138,4 @@ // ----------------------------------------------------------------------------- QString fullVersionString() { return fmt ("v%1 %2", versionString(), versionMoniker()); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void assertionFailure (const char* file, int line, const char* funcname, const char* expr) -{ str errmsg = fmt ("File: %1\nLine: %2:\nFunction %3:\n\nAssertion `%4' failed", - file, line, funcname, expr); - -#ifndef RELEASE - errmsg += ", aborting."; -#else - errmsg += "."; -#endif - - printf ("%s\n", errmsg.toStdString().c_str()); - -#ifndef RELEASE - if (g_win) - g_win->deleteLater(); - - errmsg.replace ("\n", "
"); - - QMessageBox box (null); - const QMessageBox::StandardButton btn = QMessageBox::Close; - box.setWindowTitle ("Fatal Error"); - box.setIconPixmap (getIcon ("bomb")); - box.setWindowIcon (getIcon ("ldforge")); - box.setText (errmsg); - box.addButton (btn); - box.button (btn)->setText ("Damn it"); - box.setDefaultButton (btn); - box.exec(); - abort(); -#endif } \ No newline at end of file diff -r adab82ab13a5 -r 29eb671b34f6 src/messagelog.cpp --- a/src/messagelog.cpp Fri Oct 18 18:16:54 2013 +0300 +++ b/src/messagelog.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -118,8 +118,10 @@ // ----------------------------------------------------------------------------- void DoLog (std::initializer_list args) { const str msg = DoFormat (args); - g_win->addMessage (msg); + + if (g_win) + g_win->addMessage (msg); // Also print it to stdout - print ("%1\n", msg); + fprint (stdout, "%1", msg); } diff -r adab82ab13a5 -r 29eb671b34f6 src/misc.h --- a/src/misc.h Fri Oct 18 18:16:54 2013 +0300 +++ b/src/misc.h Fri Oct 18 21:52:09 2013 +0300 @@ -19,10 +19,10 @@ #ifndef MISC_H #define MISC_H +#include #include "config.h" #include "common.h" #include "types.h" -#include #define NUM_PRIMES 500 diff -r adab82ab13a5 -r 29eb671b34f6 src/primitives.cpp --- a/src/primitives.cpp Fri Oct 18 18:16:54 2013 +0300 +++ b/src/primitives.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -49,7 +49,7 @@ // ============================================================================= // ----------------------------------------------------------------------------- void loadPrimitives() -{ print ("Loading primitives...\n"); +{ log ("Loading primitives...\n"); loadPrimitiveCatgories(); // Try to load prims.cfg diff -r adab82ab13a5 -r 29eb671b34f6 src/types.cpp --- a/src/types.cpp Fri Oct 18 18:16:54 2013 +0300 +++ b/src/types.cpp Fri Oct 18 21:52:09 2013 +0300 @@ -204,9 +204,9 @@ void matrix::puts() const { for (short i = 0; i < 3; ++i) { for (short j = 0; j < 3; ++j) - print ("%1\t", m_vals[ (i * 3) + j]); + log ("%1\t", m_vals[ (i * 3) + j]); - print ("\n"); + log ("\n"); } } diff -r adab82ab13a5 -r 29eb671b34f6 src/types.h --- a/src/types.h Fri Oct 18 18:16:54 2013 +0300 +++ b/src/types.h Fri Oct 18 21:52:09 2013 +0300 @@ -323,14 +323,12 @@ void DoLog (std::initializer_list args); // Macros to access these functions -# ifndef IN_IDE_PARSER +# ifndef IN_IDE_PARSER #define fmt(...) DoFormat ({__VA_ARGS__}) -# define print(...) doPrint (g_file_stdout, {__VA_ARGS__}) # define fprint(F, ...) doPrint (F, {__VA_ARGS__}) -# define log(...) DoLog({ __VA_ARGS__ }); +# define log(...) DoLog({ __VA_ARGS__ }) #else str fmt (const char* fmtstr, ...); -void print (const char* fmtstr, ...); void fprint (File& f, const char* fmtstr, ...); void log (const char* fmtstr, ...); #endif diff -r adab82ab13a5 -r 29eb671b34f6 ui/bombbox.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/bombbox.ui Fri Oct 18 21:52:09 2013 +0300 @@ -0,0 +1,104 @@ + + + BombBox + + + + 0 + 0 + 676 + 569 + + + + Fatal Error + + + + + + + + + + + + + :/icons/bomb.png + + + + + + + Qt::Vertical + + + + 20 + 138 + + + + + + + + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + + buttonBox + accepted() + BombBox + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + BombBox + reject() + + + 316 + 260 + + + 286 + 274 + + + + +