diff --git a/configure.in b/configure.in index 9928373..204aaf1 100644 --- a/configure.in +++ b/configure.in @@ -22,6 +22,9 @@ AC_PROG_MAKE_SET AC_PROG_RANLIB PKG_PROG_PKG_CONFIG +AC_CHECK_HEADERS_ONCE(execinfo.h) +AX_CXX_GCC_ABI_DEMANGLE + dnl -------------------------------------------------------------- dnl Checks for compilation flags dnl -------------------------------------------------------------- diff --git a/dic/Makefile.am b/dic/Makefile.am index 47295ca..6c6c492 100644 --- a/dic/Makefile.am +++ b/dic/Makefile.am @@ -23,6 +23,7 @@ localedir = $(datadir)/locale AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" -I$(top_srcdir) -I../intl -I$(top_srcdir)/intl $(INCICONV) libdic_a_SOURCES = \ + base_exception.cpp base_exception.h \ dic_exception.cpp dic_exception.h \ header.cpp header.h \ dic_internals.h \ @@ -30,6 +31,7 @@ libdic_a_SOURCES = \ dic.cpp dic.h \ dic_search.cpp \ encoding.cpp encoding.h \ + stacktrace.cpp stacktrace.h \ automaton.cpp automaton.h \ regexp.cpp regexp.h \ grammar.cpp grammar.h \ diff --git a/dic/base_exception.cpp b/dic/base_exception.cpp new file mode 100644 index 0000000..06cf5c0 --- /dev/null +++ b/dic/base_exception.cpp @@ -0,0 +1,44 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2010 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#include "base_exception.h" +#include "stacktrace.h" + +using namespace std; + + +BaseException::BaseException(const string &iMessage) + : m_message(iMessage) +{ + m_stack = StackTrace::GetStack(); +} + + +const char *BaseException::what() const throw() +{ + return m_message.c_str(); +} + + +string BaseException::getStackTrace() const +{ + return m_stack; +} + diff --git a/dic/base_exception.h b/dic/base_exception.h new file mode 100644 index 0000000..1d1371f --- /dev/null +++ b/dic/base_exception.h @@ -0,0 +1,46 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2010 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#ifndef BASE_EXCEPTION_H_ +#define BASE_EXCEPTION_H_ + +#include +#include + + +/** + * Base exception class for all the exception classes in Eliot. + * It provides a stack trace. + */ +class BaseException: public std::exception +{ + public: + BaseException(const std::string &iMessage); + ~BaseException() throw() {} + virtual const char *what() const throw(); + + std::string getStackTrace() const; + + private: + std::string m_message; + std::string m_stack; +}; + +#endif diff --git a/dic/dic_exception.cpp b/dic/dic_exception.cpp index dabc84c..9b9e3f2 100644 --- a/dic/dic_exception.cpp +++ b/dic/dic_exception.cpp @@ -1,6 +1,6 @@ /***************************************************************************** * Eliot - * Copyright (C) 2007 Olivier Teulière + * Copyright (C) 2007-2010 Olivier Teulière * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify @@ -24,17 +24,11 @@ using namespace std; DicException::DicException(const string &iMessage) - : m_message(iMessage) + : BaseException(iMessage) { } -const char *DicException::what() const throw() -{ - return m_message.c_str(); -} - - InvalidRegexpException::InvalidRegexpException(const string &iMessage) : DicException(iMessage) { diff --git a/dic/dic_exception.h b/dic/dic_exception.h index 6f69b4f..de94822 100644 --- a/dic/dic_exception.h +++ b/dic/dic_exception.h @@ -1,6 +1,6 @@ /***************************************************************************** * Eliot - * Copyright (C) 2007 Olivier Teulière + * Copyright (C) 2007-2010 Olivier Teulière * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify @@ -21,24 +21,19 @@ #ifndef DIC_EXCEPTION_H_ #define DIC_EXCEPTION_H_ -#include #include +#include "base_exception.h" /** * Exception class for the dictionary. - * It simply inherits from the standard exception and overrides + * It simply inherits from the base exception and overrides * its what() method. */ -class DicException: public std::exception +class DicException: public BaseException { public: DicException(const std::string &iMessage); - ~DicException() throw() {} - virtual const char *what() const throw(); - - private: - std::string m_message; }; @@ -46,7 +41,6 @@ class InvalidRegexpException : public DicException { public: InvalidRegexpException(const std::string &iMessage); - ~InvalidRegexpException() throw() {} }; #endif diff --git a/dic/stacktrace.cpp b/dic/stacktrace.cpp new file mode 100644 index 0000000..dc1c4da --- /dev/null +++ b/dic/stacktrace.cpp @@ -0,0 +1,91 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2010 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#include "config.h" +#include +#include +#include +#include + +#include "stacktrace.h" + +#ifdef HAVE_EXECINFO_H +# include +#endif +#ifdef HAVE_GCC_ABI_DEMANGLE +# include +#endif + +using namespace std; + + +string StackTrace::GetStack() +{ +#if defined HAVE_EXECINFO_H && defined(DEBUG) + static const int MAX_FRAMES = 42; + void *frames[MAX_FRAMES]; + // Get the frames + int nb = backtrace(frames, MAX_FRAMES); + // Get the corresponding symbols (ignoring the first frame) + char **symbols = backtrace_symbols(frames + 1, nb - 1); + // Demangle the symbols and build a nice stack trace + ostringstream oss; + for (int i = 0; i < nb - 1; ++i) + { + oss << " at " << Demangle(symbols[i]) << endl; + } + + return oss.str(); +#else + return ""; +#endif +} + + +// See http://tombarta.wordpress.com/2008/08/01/c-stack-traces-with-gcc/ +// and http://mykospark.net/2009/09/runtime-backtrace-in-c-with-name-demangling/ +// for more details +string StackTrace::Demangle(char *symbol) +{ +#if defined HAVE_GCC_ABI_DEMANGLE && defined(DEBUG) + char temp1[200]; + char temp2[200]; + if (sscanf(symbol, "%199[^(]%*[^_]%199[^)+]", temp1, temp2) == 2) + { + // Try to demangle a C++ name + int status; + char *demangled = abi::__cxa_demangle(temp2, NULL, NULL, &status); + if (demangled != NULL) + { + string result = temp1 + string(": ") + demangled; + free(demangled); + return result; + } + // Try to demangle a C name + else if (sscanf(symbol, "%199s", temp2) == 1) + { + return temp1 + string(": ") + temp2; + } + } + + // Everything else failed, return the symbol +#endif + return symbol; +} diff --git a/dic/stacktrace.h b/dic/stacktrace.h new file mode 100644 index 0000000..3fbb866 --- /dev/null +++ b/dic/stacktrace.h @@ -0,0 +1,38 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2010 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#ifndef STACK_TRACE_H_ +#define STACK_TRACE_H_ + +#include + +using std::string; + + +class StackTrace +{ +public: + static string GetStack(); + +private: + static string Demangle(char *symbol); +}; + +#endif diff --git a/game/game_exception.cpp b/game/game_exception.cpp index 23ac62b..beccfc5 100644 --- a/game/game_exception.cpp +++ b/game/game_exception.cpp @@ -1,6 +1,6 @@ /***************************************************************************** * Eliot - * Copyright (C) 2007 Olivier Teulière + * Copyright (C) 2007-2010 Olivier Teulière * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify @@ -24,17 +24,11 @@ using namespace std; GameException::GameException(const string &iMessage) - : m_message(iMessage) + : BaseException(iMessage) { } -const char *GameException::what() const throw() -{ - return m_message.c_str(); -} - - EndGameException::EndGameException(const string &iMessage) : GameException(iMessage) { diff --git a/game/game_exception.h b/game/game_exception.h index 1b40029..206adf1 100644 --- a/game/game_exception.h +++ b/game/game_exception.h @@ -1,6 +1,6 @@ /***************************************************************************** * Eliot - * Copyright (C) 2007 Olivier Teulière + * Copyright (C) 2007-2010 Olivier Teulière * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify @@ -21,24 +21,19 @@ #ifndef GAME_EXCEPTION_H_ #define GAME_EXCEPTION_H_ -#include #include +#include "base_exception.h" /** * Exception class for the Game library. - * It simply inherits from the standard exception and overrides + * It simply inherits from the base exception and overrides * its what() method. */ -class GameException: public std::exception +class GameException: public BaseException { public: GameException(const std::string &iMessage); - ~GameException() throw() {} - virtual const char *what() const throw(); - - private: - std::string m_message; }; diff --git a/m4/ax_cxx_gcc_abi_demangle.m4 b/m4/ax_cxx_gcc_abi_demangle.m4 new file mode 100644 index 0000000..b2a4b43 --- /dev/null +++ b/m4/ax_cxx_gcc_abi_demangle.m4 @@ -0,0 +1,58 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_cxx_gcc_abi_demangle.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_GCC_ABI_DEMANGLE +# +# DESCRIPTION +# +# If the compiler supports GCC C++ ABI name demangling (has header +# cxxabi.h and abi::__cxa_demangle() function), define +# HAVE_GCC_ABI_DEMANGLE +# +# Adapted from AX_CXX_RTTI by Luc Maisonobe +# +# LICENSE +# +# Copyright (c) 2008 Neil Ferguson +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AC_DEFUN([AX_CXX_GCC_ABI_DEMANGLE], +[AC_CACHE_CHECK(whether the compiler supports GCC C++ ABI name demangling, +ax_cv_cxx_gcc_abi_demangle, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([#include +#include +#include +#include + +template +class A {}; +],[A instance; +int status = 0; +char* c_name = 0; + +c_name = abi::__cxa_demangle(typeid(instance).name(), 0, 0, &status); + +std::string name(c_name); +free(c_name); + +return name == "A"; +], + ax_cv_cxx_gcc_abi_demangle=yes, ax_cv_cxx_gcc_abi_demangle=no) + AC_LANG_RESTORE +]) +if test "$ax_cv_cxx_gcc_abi_demangle" = yes; then + AC_DEFINE(HAVE_GCC_ABI_DEMANGLE,1, + [define if the compiler supports GCC C++ ABI name demangling]) +fi +]) diff --git a/qt/Makefile.am b/qt/Makefile.am index 70dd188..bb5bc42 100644 --- a/qt/Makefile.am +++ b/qt/Makefile.am @@ -104,6 +104,8 @@ BUILT_SOURCES = $(nodist_eliot_SOURCES) MOSTLYCLEANFILES = $(nodist_eliot_SOURCES) eliot_LDADD = ../game/libgame.a ../dic/libdic.a @QT_LIBS@ @LIBINTL@ @LIBCONFIG_LIBS@ @ARABICA_LIBS@ @EXPAT_LDFLAGS@ +# Needed for proper stack trace handling +eliot_LDFLAGS = -rdynamic # Generate a cpp file from the resources resources.cpp: eliot.qrc $(RESOURCES) diff --git a/qt/main.cpp b/qt/main.cpp index ac6cde9..163f94d 100644 --- a/qt/main.cpp +++ b/qt/main.cpp @@ -21,9 +21,13 @@ #include "config.h" #include +#include +#include #include #include #include +#include "base_exception.h" +#include "stacktrace.h" #include "main_window.h" #ifdef WIN32 # include @@ -32,11 +36,48 @@ # include #endif -using std::string; +#ifdef HAVE_EXECINFO_H +# include +# include +#endif +using namespace std; + + +static void bt_sighandler(int); + +// Custom QApplication to catch and log exceptions properly +// See http://forum.qtfr.org/viewtopic.php?id=7615 +class MyApplication : public QApplication +{ +public: + MyApplication(int argc, char **argv) + : QApplication(argc, argv) + {} + + virtual bool notify(QObject *receiver, QEvent *event) + { + try + { + return QApplication::notify(receiver, event); + } + catch (const BaseException &e) + { + cerr << "Exception caught: " << e.what() << endl; + cerr << e.getStackTrace() << endl; + return false; + } + } +}; int main(int argc, char **argv) { +#ifdef HAVE_EXECINFO_H + // Install a custom signal handler to print a backtrace when crashing + // See http://www.linuxjournal.com/article/6391 for inspiration + signal(SIGSEGV, &bt_sighandler); +#endif + // On Mac, running Eliot from the dock does not automatically set the LANG // variable, so we do it ourselves. // Note: The following block of code is copied from VLC, and slightly @@ -77,7 +118,7 @@ int main(int argc, char **argv) #endif #endif - QApplication app(argc, argv); + MyApplication app(argc, argv); app.setWindowIcon(QIcon(":/images/eliot.xpm")); #ifdef ENABLE_NLS @@ -121,3 +162,17 @@ int main(int argc, char **argv) qmain.show(); return app.exec(); } + +#ifdef HAVE_EXECINFO_H +static void bt_sighandler(int signum) +{ + cerr << "Segmentation fault!" << endl; + cerr << "Backtrace:" << endl; + cerr << StackTrace::GetStack() << endl; + + // Restore the default handler to generate a nice core dump + signal(signum, SIG_DFL); + raise(signum); +} +#endif +