Print to stderr the complete stack trace in case of exception or segmentation fault.

This feature will only be activated in debug mode, and if available on the platform.
The symbols will even be demangled if possible (i.e. if compiled with g++).
This commit is contained in:
Olivier Teulière 2010-10-17 21:23:39 +00:00
parent 892d0ae0cb
commit c3785555b9
13 changed files with 353 additions and 37 deletions

View file

@ -22,6 +22,9 @@ AC_PROG_MAKE_SET
AC_PROG_RANLIB AC_PROG_RANLIB
PKG_PROG_PKG_CONFIG PKG_PROG_PKG_CONFIG
AC_CHECK_HEADERS_ONCE(execinfo.h)
AX_CXX_GCC_ABI_DEMANGLE
dnl -------------------------------------------------------------- dnl --------------------------------------------------------------
dnl Checks for compilation flags dnl Checks for compilation flags
dnl -------------------------------------------------------------- dnl --------------------------------------------------------------

View file

@ -23,6 +23,7 @@ localedir = $(datadir)/locale
AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" -I$(top_srcdir) -I../intl -I$(top_srcdir)/intl $(INCICONV) AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" -I$(top_srcdir) -I../intl -I$(top_srcdir)/intl $(INCICONV)
libdic_a_SOURCES = \ libdic_a_SOURCES = \
base_exception.cpp base_exception.h \
dic_exception.cpp dic_exception.h \ dic_exception.cpp dic_exception.h \
header.cpp header.h \ header.cpp header.h \
dic_internals.h \ dic_internals.h \
@ -30,6 +31,7 @@ libdic_a_SOURCES = \
dic.cpp dic.h \ dic.cpp dic.h \
dic_search.cpp \ dic_search.cpp \
encoding.cpp encoding.h \ encoding.cpp encoding.h \
stacktrace.cpp stacktrace.h \
automaton.cpp automaton.h \ automaton.cpp automaton.h \
regexp.cpp regexp.h \ regexp.cpp regexp.h \
grammar.cpp grammar.h \ grammar.cpp grammar.h \

44
dic/base_exception.cpp Normal file
View file

@ -0,0 +1,44 @@
/*****************************************************************************
* Eliot
* Copyright (C) 2010 Olivier Teulière
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
*
* 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;
}

46
dic/base_exception.h Normal file
View file

@ -0,0 +1,46 @@
/*****************************************************************************
* Eliot
* Copyright (C) 2010 Olivier Teulière
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
*
* 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 <exception>
#include <string>
/**
* 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

View file

@ -1,6 +1,6 @@
/***************************************************************************** /*****************************************************************************
* Eliot * Eliot
* Copyright (C) 2007 Olivier Teulière * Copyright (C) 2007-2010 Olivier Teulière
* Authors: Olivier Teulière <ipkiss @@ gmail.com> * Authors: Olivier Teulière <ipkiss @@ gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -24,17 +24,11 @@ using namespace std;
DicException::DicException(const string &iMessage) 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) InvalidRegexpException::InvalidRegexpException(const string &iMessage)
: DicException(iMessage) : DicException(iMessage)
{ {

View file

@ -1,6 +1,6 @@
/***************************************************************************** /*****************************************************************************
* Eliot * Eliot
* Copyright (C) 2007 Olivier Teulière * Copyright (C) 2007-2010 Olivier Teulière
* Authors: Olivier Teulière <ipkiss @@ gmail.com> * Authors: Olivier Teulière <ipkiss @@ gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -21,24 +21,19 @@
#ifndef DIC_EXCEPTION_H_ #ifndef DIC_EXCEPTION_H_
#define DIC_EXCEPTION_H_ #define DIC_EXCEPTION_H_
#include <exception>
#include <string> #include <string>
#include "base_exception.h"
/** /**
* Exception class for the dictionary. * 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. * its what() method.
*/ */
class DicException: public std::exception class DicException: public BaseException
{ {
public: public:
DicException(const std::string &iMessage); 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: public:
InvalidRegexpException(const std::string &iMessage); InvalidRegexpException(const std::string &iMessage);
~InvalidRegexpException() throw() {}
}; };
#endif #endif

91
dic/stacktrace.cpp Normal file
View file

@ -0,0 +1,91 @@
/*****************************************************************************
* Eliot
* Copyright (C) 2010 Olivier Teulière
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
*
* 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 <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "stacktrace.h"
#ifdef HAVE_EXECINFO_H
# include <execinfo.h>
#endif
#ifdef HAVE_GCC_ABI_DEMANGLE
# include <cxxabi.h>
#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;
}

38
dic/stacktrace.h Normal file
View file

@ -0,0 +1,38 @@
/*****************************************************************************
* Eliot
* Copyright (C) 2010 Olivier Teulière
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
*
* 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 <string>
using std::string;
class StackTrace
{
public:
static string GetStack();
private:
static string Demangle(char *symbol);
};
#endif

View file

@ -1,6 +1,6 @@
/***************************************************************************** /*****************************************************************************
* Eliot * Eliot
* Copyright (C) 2007 Olivier Teulière * Copyright (C) 2007-2010 Olivier Teulière
* Authors: Olivier Teulière <ipkiss @@ gmail.com> * Authors: Olivier Teulière <ipkiss @@ gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -24,17 +24,11 @@ using namespace std;
GameException::GameException(const string &iMessage) 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) EndGameException::EndGameException(const string &iMessage)
: GameException(iMessage) : GameException(iMessage)
{ {

View file

@ -1,6 +1,6 @@
/***************************************************************************** /*****************************************************************************
* Eliot * Eliot
* Copyright (C) 2007 Olivier Teulière * Copyright (C) 2007-2010 Olivier Teulière
* Authors: Olivier Teulière <ipkiss @@ gmail.com> * Authors: Olivier Teulière <ipkiss @@ gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -21,24 +21,19 @@
#ifndef GAME_EXCEPTION_H_ #ifndef GAME_EXCEPTION_H_
#define GAME_EXCEPTION_H_ #define GAME_EXCEPTION_H_
#include <exception>
#include <string> #include <string>
#include "base_exception.h"
/** /**
* Exception class for the Game library. * 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. * its what() method.
*/ */
class GameException: public std::exception class GameException: public BaseException
{ {
public: public:
GameException(const std::string &iMessage); GameException(const std::string &iMessage);
~GameException() throw() {}
virtual const char *what() const throw();
private:
std::string m_message;
}; };

View file

@ -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 <nferguso@eso.org>
#
# 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 <typeinfo>
#include <cxxabi.h>
#include <string>
#include <cstdlib>
template<typename TYPE>
class A {};
],[A<int> 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<int>";
],
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
])

View file

@ -104,6 +104,8 @@ BUILT_SOURCES = $(nodist_eliot_SOURCES)
MOSTLYCLEANFILES = $(nodist_eliot_SOURCES) MOSTLYCLEANFILES = $(nodist_eliot_SOURCES)
eliot_LDADD = ../game/libgame.a ../dic/libdic.a @QT_LIBS@ @LIBINTL@ @LIBCONFIG_LIBS@ @ARABICA_LIBS@ @EXPAT_LDFLAGS@ 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 # Generate a cpp file from the resources
resources.cpp: eliot.qrc $(RESOURCES) resources.cpp: eliot.qrc $(RESOURCES)

View file

@ -21,9 +21,13 @@
#include "config.h" #include "config.h"
#include <string> #include <string>
#include <exception>
#include <iostream>
#include <QApplication> #include <QApplication>
#include <QLocale> #include <QLocale>
#include <QTranslator> #include <QTranslator>
#include "base_exception.h"
#include "stacktrace.h"
#include "main_window.h" #include "main_window.h"
#ifdef WIN32 #ifdef WIN32
# include <windows.h> # include <windows.h>
@ -32,11 +36,48 @@
# include <CoreFoundation/CoreFoundation.h> # include <CoreFoundation/CoreFoundation.h>
#endif #endif
using std::string; #ifdef HAVE_EXECINFO_H
# include <signal.h>
# include <execinfo.h>
#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) 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 // On Mac, running Eliot from the dock does not automatically set the LANG
// variable, so we do it ourselves. // variable, so we do it ourselves.
// Note: The following block of code is copied from VLC, and slightly // Note: The following block of code is copied from VLC, and slightly
@ -77,7 +118,7 @@ int main(int argc, char **argv)
#endif #endif
#endif #endif
QApplication app(argc, argv); MyApplication app(argc, argv);
app.setWindowIcon(QIcon(":/images/eliot.xpm")); app.setWindowIcon(QIcon(":/images/eliot.xpm"));
#ifdef ENABLE_NLS #ifdef ENABLE_NLS
@ -121,3 +162,17 @@ int main(int argc, char **argv)
qmain.show(); qmain.show();
return app.exec(); 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