mirror of https://github.com/KLayout/klayout.git
Enhanced crash handler on Linux
* Override Ruby's crash handler to stop blaming Ruby for KLayout segfaults ... * Using addr2line on Linux to obtain DWARF debug info if available * A more elaborate crash handler dialog
This commit is contained in:
parent
80226e8c1c
commit
dfbe0b3122
|
|
@ -671,11 +671,9 @@ Application::Application (int &argc, char **argv, bool non_ui_mode)
|
|||
|
||||
}
|
||||
|
||||
if (! m_no_gui) {
|
||||
// Install the signal handlers after the interpreters, so we can be sure we
|
||||
// installed our handler.
|
||||
install_signal_handlers ();
|
||||
}
|
||||
// Install the signal handlers after the interpreters, so we can be sure we
|
||||
// installed our handler.
|
||||
install_signal_handlers ();
|
||||
|
||||
lay::SaltController *sc = lay::SaltController::instance ();
|
||||
lay::TechnologyController *tc = lay::TechnologyController::instance ();
|
||||
|
|
|
|||
|
|
@ -28,16 +28,39 @@
|
|||
namespace lay
|
||||
{
|
||||
|
||||
CrashMessage::CrashMessage (QWidget *parent, bool can_resume, const QString &stack_trace)
|
||||
CrashMessage::CrashMessage (QWidget *parent, bool can_resume, const QString &t)
|
||||
: QDialog (parent, Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint)
|
||||
{
|
||||
setupUi (this);
|
||||
m_cancel_pressed = false;
|
||||
|
||||
text->setPlainText (stack_trace);
|
||||
text->setPlainText (t);
|
||||
set_can_resume (can_resume);
|
||||
|
||||
if (! can_resume) {
|
||||
buttonBox->removeButton (buttonBox->button (QDialogButtonBox::Ok));
|
||||
}
|
||||
connect (buttonBox->button (QDialogButtonBox::Cancel), SIGNAL (pressed ()), this, SLOT (cancel_pressed ()));
|
||||
}
|
||||
|
||||
CrashMessage::~CrashMessage ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
CrashMessage::set_can_resume (bool f)
|
||||
{
|
||||
buttonBox->button (QDialogButtonBox::Ok)->setVisible (f);
|
||||
}
|
||||
|
||||
void
|
||||
CrashMessage::set_text (const QString &t)
|
||||
{
|
||||
text->setPlainText (t);
|
||||
}
|
||||
|
||||
void
|
||||
CrashMessage::cancel_pressed ()
|
||||
{
|
||||
m_cancel_pressed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,14 +38,53 @@ namespace lay
|
|||
class LAY_PUBLIC CrashMessage
|
||||
: public QDialog, private Ui::CrashMessage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Instantiate a dialog
|
||||
*
|
||||
* @param can_resume If true, an "Ok" button is provided
|
||||
* @param stack_trace The stack trace message shown in the window
|
||||
* @param text The message shown in the window
|
||||
*/
|
||||
CrashMessage (QWidget *parent, bool can_resume, const QString &stack_trace);
|
||||
CrashMessage (QWidget *parent, bool can_resume, const QString &text);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~CrashMessage ();
|
||||
|
||||
/**
|
||||
* @brief Configures the dialog for "can resume" or "can't resume"
|
||||
*/
|
||||
void set_can_resume (bool f);
|
||||
|
||||
/**
|
||||
* @brief Sets the text
|
||||
*/
|
||||
void set_text (const QString &text);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the Cancel button was pressed
|
||||
*/
|
||||
bool is_cancel_pressed ()
|
||||
{
|
||||
return m_cancel_pressed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets the flag indicating whether Cancel was pressed
|
||||
*/
|
||||
void reset_cancel_pressed ()
|
||||
{
|
||||
m_cancel_pressed = false;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void cancel_pressed ();
|
||||
|
||||
private:
|
||||
bool m_cancel_pressed;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,10 @@
|
|||
#include "laySignalHandler.h"
|
||||
#include "layCrashMessage.h"
|
||||
#include "layVersion.h"
|
||||
#include "layApplication.h"
|
||||
#include "tlException.h"
|
||||
#include "tlString.h"
|
||||
#include "tlLog.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
|
|
@ -43,7 +46,9 @@
|
|||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
#include <cstdio>
|
||||
#include <QFileInfo>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
|
@ -268,49 +273,156 @@ void signal_handler (int signo, siginfo_t *si, void *)
|
|||
|
||||
size_t nptrs = backtrace (array, sizeof (array) / sizeof (array[0]));
|
||||
|
||||
QString text;
|
||||
text += QObject::tr ("Signal number: %1\n").arg (signo);
|
||||
text += QObject::tr ("Address: 0x%1\n").arg ((size_t) si->si_addr, 0, 16);
|
||||
text += QObject::tr ("Program Version: ") +
|
||||
QString::fromUtf8 (lay::Version::name ()) +
|
||||
QString::fromUtf8 (" ") +
|
||||
QString::fromUtf8 (lay::Version::version ()) +
|
||||
QString::fromUtf8 (" (") +
|
||||
QString::fromUtf8 (lay::Version::subversion ()) +
|
||||
QString::fromUtf8 (")");
|
||||
text += QString::fromUtf8 ("\n");
|
||||
text += QObject::tr ("Backtrace:\n");
|
||||
std::string text;
|
||||
text += tl::sprintf ("Signal number: %d\n", signo);
|
||||
text += tl::sprintf ("Address: 0x%lx\n", (unsigned long) si->si_addr);
|
||||
text += std::string ("Program Version: ") +
|
||||
lay::Version::name () + " " +
|
||||
lay::Version::version () + " (" + lay::Version::subversion () + ")\n";
|
||||
|
||||
std::auto_ptr<CrashMessage> msg;
|
||||
|
||||
bool has_gui = lay::Application::instance () && lay::Application::instance ()->has_gui ();
|
||||
|
||||
if (has_gui) {
|
||||
msg.reset (new CrashMessage (0, false, tl::to_qstring (text) + QObject::tr ("\nCollecting backtrace ..")));
|
||||
msg->show ();
|
||||
lay::Application::instance ()->setOverrideCursor (Qt::WaitCursor);
|
||||
}
|
||||
|
||||
text += std::string ("\nBacktrace:\n");
|
||||
|
||||
#if 0
|
||||
|
||||
// the approach with backtrace_symbols - this does not resolve shared object symbols
|
||||
char **symbols = backtrace_symbols (array, nptrs);
|
||||
if (symbols == NULL) {
|
||||
text += QObject::tr ("-- Unable to obtain stack trace --");
|
||||
text += "-- Unable to obtain stack trace --\n";
|
||||
} else {
|
||||
for (size_t i = 2; i < nptrs; i++) {
|
||||
text += QString::fromUtf8 (symbols [i]) + QString::fromUtf8 ("\n");
|
||||
text += std::string (symbols [i]) + "\n";
|
||||
}
|
||||
}
|
||||
free(symbols);
|
||||
|
||||
// YES! I! KNOW!
|
||||
// In a signal handler you shall not do fancy stuff (in particular not
|
||||
// open dialogs) nor shall you throw exceptions! But that scheme appears to
|
||||
// be working since in most cases the signal is raised from our code (hence
|
||||
// from our stack frames) and everything is better than just core dumping.
|
||||
// Isn't it?
|
||||
#else
|
||||
|
||||
CrashMessage msg (0, can_resume, text);
|
||||
if (! msg.exec ()) {
|
||||
// the more elaborate approach using the addr2line external tool to obtain debug information
|
||||
// (if available)
|
||||
|
||||
_exit (signo);
|
||||
const char *addr2line_call = "addr2line -C -s -f -e '%s' 0x%lx";
|
||||
|
||||
bool has_addr2line = true;
|
||||
for (size_t i = 0; i < nptrs; ++i) {
|
||||
|
||||
if (has_gui) {
|
||||
lay::Application::instance ()->processEvents ();
|
||||
if (msg->is_cancel_pressed ()) {
|
||||
text += "...\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Dl_info info;
|
||||
dladdr (array [i], &info);
|
||||
|
||||
if (info.dli_fname) {
|
||||
|
||||
char sym [1024], source [1024];
|
||||
sym[0] = 0;
|
||||
source[0] = 0;
|
||||
|
||||
if (has_addr2line) {
|
||||
|
||||
// two tries: one with the relativew address (for shared object) and one with
|
||||
// absolute address.
|
||||
// TODO: is there a better way to decide how to use addr2line (with executables)?
|
||||
for (int abs_addr = 0; abs_addr < 2; ++abs_addr) {
|
||||
|
||||
std::string cmd = tl::sprintf (addr2line_call, info.dli_fname, size_t (array[i]) - (abs_addr ? 0 : size_t (info.dli_fbase)));
|
||||
FILE *addr2line_out = popen (cmd.c_str (), "r");
|
||||
if (! addr2line_out) {
|
||||
has_addr2line = false;
|
||||
}
|
||||
|
||||
if (has_addr2line && ! fgets (sym, sizeof (sym) - 1, addr2line_out)) {
|
||||
has_addr2line = false;
|
||||
}
|
||||
if (has_addr2line && ! fgets (source, sizeof (source) - 1, addr2line_out)) {
|
||||
has_addr2line = false;
|
||||
}
|
||||
|
||||
int l;
|
||||
l = strlen (sym);
|
||||
if (l > 0 && sym[l - 1] == '\n') {
|
||||
sym[l - 1] = 0;
|
||||
}
|
||||
l = strlen (source);
|
||||
if (l > 0 && source[l - 1] == '\n') {
|
||||
source[l - 1] = 0;
|
||||
}
|
||||
|
||||
if (addr2line_out) {
|
||||
fclose (addr2line_out);
|
||||
}
|
||||
|
||||
// addr2line returns '??' on missing symbol - in that case use absolute address mode
|
||||
if (sym[0] != '?') {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (has_addr2line) {
|
||||
text += tl::sprintf ("%s +0x%lx %s [%s]\n", info.dli_fname, size_t (array[i]) - size_t (info.dli_fbase), (const char *) sym, (const char *) source);
|
||||
} else if (info.dli_sname) {
|
||||
text += tl::sprintf ("%s +0x%lx %s\n", info.dli_fname, size_t (array[i]) - size_t (info.dli_fbase), info.dli_sname);
|
||||
} else {
|
||||
text += tl::sprintf ("%s +0x%lx\n", info.dli_fname, size_t (array[i]) - size_t (info.dli_fbase));
|
||||
}
|
||||
|
||||
} else {
|
||||
text += tl::sprintf ("0x%lx\n", (unsigned long)array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (has_gui) {
|
||||
|
||||
lay::Application::instance ()->setOverrideCursor (QCursor ());
|
||||
|
||||
// YES! I! KNOW!
|
||||
// In a signal handler you shall not do fancy stuff (in particular not
|
||||
// open dialogs) nor shall you throw exceptions! But that scheme appears to
|
||||
// be working since in most cases the signal is raised from our code (hence
|
||||
// from our stack frames) and everything is better than just core dumping.
|
||||
// Isn't it?
|
||||
|
||||
msg->set_text (tl::to_qstring (text));
|
||||
msg->set_can_resume (can_resume);
|
||||
|
||||
if (! msg->exec ()) {
|
||||
|
||||
_exit (signo);
|
||||
|
||||
} else {
|
||||
|
||||
sigset_t x;
|
||||
sigemptyset (&x);
|
||||
sigaddset(&x, signo);
|
||||
sigprocmask(SIG_UNBLOCK, &x, NULL);
|
||||
|
||||
throw tl::CancelException ();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
sigset_t x;
|
||||
sigemptyset (&x);
|
||||
sigaddset(&x, signo);
|
||||
sigprocmask(SIG_UNBLOCK, &x, NULL);
|
||||
|
||||
throw tl::CancelException ();
|
||||
tl::error << text << tl::noendl;
|
||||
_exit (signo);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue