Use forked process to handle crashes (#1443)

This commit is contained in:
optizone 2019-04-09 22:33:13 +03:00 committed by Florian Märkl
parent 1cb314d674
commit c2deabee5d
4 changed files with 83 additions and 114 deletions

View File

@ -1,4 +1,5 @@
#include "common/PythonManager.h" #include "common/PythonManager.h"
#include "common/CrashHandler.h"
#include "CutterApplication.h" #include "CutterApplication.h"
#include "plugins/PluginManager.h" #include "plugins/PluginManager.h"
#include "CutterConfig.h" #include "CutterConfig.h"

View File

@ -21,6 +21,13 @@ static void migrateSettings(QSettings &newSettings)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
if (argc >= 3 && QString::fromLocal8Bit(argv[1]) == "--start-crash-handler") {
QApplication app(argc, argv);
QString dumpLocation = QString::fromLocal8Bit(argv[2]);
showCrashDialog(dumpLocation);
return 0;
}
initCrashHandler(); initCrashHandler();
qRegisterMetaType<QList<StringDescription>>(); qRegisterMetaType<QList<StringDescription>>();

View File

@ -1,19 +1,20 @@
#include "CrashHandler.h" #include "CrashHandler.h"
#ifdef CUTTER_ENABLE_CRASH_REPORTS
#include "BugReporting.h" #include "BugReporting.h"
#include <QStandardPaths>
#include <QApplication>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <QFileDialog> #include <QFileDialog>
#include <signal.h> #include <QStandardPaths>
#include <QString>
#include <QTime> #include <QTime>
#ifdef CUTTER_ENABLE_CRASH_REPORTS
#include <QApplication>
#include <QString>
#include <QFile> #include <QFile>
#include <QDir> #include <QDir>
#include <QMap> #include <QMap>
#include <QProcess>
#if defined (Q_OS_LINUX) #if defined (Q_OS_LINUX)
#include "client/linux/handler/exception_handler.h" #include "client/linux/handler/exception_handler.h"
@ -23,41 +24,12 @@
#include "client/mac/handler/exception_handler.h" #include "client/mac/handler/exception_handler.h"
#endif // Q_OS #endif // Q_OS
static google_breakpad::ExceptionHandler *exceptionHandler = nullptr;
static void finishCrashHandler()
// Here will be placed crash dump at the first place {
// and then moved if needed delete exceptionHandler;
#if defined (Q_OS_LINUX) || defined (Q_OS_MACOS) }
static std::string tmpLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString();
#else
static std::wstring tmpLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdWString();
#endif
static const QMap<int, QString> sigNumDescription = {
#ifdef SIGSEGV
{ SIGSEGV, "SIGSEGV" },
#endif // SIGSEGV
#ifdef SIGILL
{ SIGILL, "SIGILL" },
#endif // SIGILL
#ifdef SIGFPE
{ SIGFPE, "SIGFPE" },
#endif // SIGFPE
#ifdef SIGABRT
{ SIGABRT, "SIGABRT" },
#endif // SIGABRT
#ifdef SIGBUS
{ SIGBUS, "SIGBUS" },
#endif // SIGBUS
#ifdef SIGPIPE
{ SIGPIPE, "SIGPIPE" },
#endif // SIGPIPE
#ifdef SIGSYS
{ SIGSYS, "SIGSYS" }
#endif // SIGSYS
};
static QString dumpFileFullPath = "";
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
// Called if crash dump was successfully created // Called if crash dump was successfully created
@ -68,9 +40,11 @@ bool callback(const wchar_t *_dump_dir,
MDRawAssertionInfo *assertion, MDRawAssertionInfo *assertion,
bool success) bool success)
{ {
QString dir = QString::fromWCharArray(_dump_dir); const QDir dir = QString::fromWCharArray(_dump_dir);
QString id = QString::fromWCharArray(_minidump_id); const QString id = QString::fromWCharArray(_minidump_id);
dumpFileFullPath = QDir(dir).filePath(id + ".dmp"); QProcess::execute(QCoreApplication::applicationFilePath()
+ " --start-crash-handler "
+ dir.filePath(id + ".dmp"));
return true; return true;
} }
#elif defined (Q_OS_LINUX) #elif defined (Q_OS_LINUX)
@ -78,7 +52,9 @@ bool callback(const wchar_t *_dump_dir,
// Saves path to file // Saves path to file
bool callback(const google_breakpad::MinidumpDescriptor &md, void *context, bool b) bool callback(const google_breakpad::MinidumpDescriptor &md, void *context, bool b)
{ {
dumpFileFullPath = md.path(); QProcess::execute(QCoreApplication::applicationFilePath()
+ " --start-crash-handler "
+ md.path());
return true; return true;
} }
#elif defined (Q_OS_MACOS) #elif defined (Q_OS_MACOS)
@ -86,54 +62,72 @@ bool callback(const google_breakpad::MinidumpDescriptor &md, void *context, bool
// Saves path to file // Saves path to file
bool callback(const char *dump_dir, const char *minidump_id, void *context, bool succeeded) bool callback(const char *dump_dir, const char *minidump_id, void *context, bool succeeded)
{ {
QString dir = QString::fromUtf8(dump_dir); const QDir dir = QString::fromUtf8(dump_dir);
QString id = QString::fromUtf8(minidump_id); const QString id = QString::fromUtf8(minidump_id);
dumpFileFullPath = QDir(dir).filePath(id + ".dmp"); QProcess::execute(QCoreApplication::applicationFilePath()
+ " --start-crash-handler "
+ dir.filePath(id + ".dmp"));
return true; return true;
} }
#endif // Q_OS #endif // Q_OS
void initCrashHandler()
/**
* @brief Writes minidump and put its name in dumpFileFullPath.
* @return true on succes
*/
bool writeMinidump()
{ {
bool ok; if (exceptionHandler) {
#if defined (Q_OS_LINUX) || defined (Q_OS_MACOS) return;
ok = google_breakpad::ExceptionHandler::WriteMinidump(tmpLocation, }
callback, // Here will be placed crash dump at the first place
nullptr); // and then moved if needed
#elif defined (Q_OS_WIN32)
ok = google_breakpad::ExceptionHandler::WriteMinidump(tmpLocation, #if defined (Q_OS_LINUX)
callback, static std::string tmpLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString();
nullptr); exceptionHandler = new google_breakpad::ExceptionHandler(google_breakpad::MinidumpDescriptor(tmpLocation),
#endif // Q_OS nullptr,
return ok; callback,
nullptr,
true,
-1);
#elif defined (Q_OS_MACOS)
static std::string tmpLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString();
exceptionHandler = new google_breakpad::ExceptionHandler(tmpLocation,
nullptr,
callback,
nullptr,
true,
nullptr);
#else
static std::wstring tmpLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdWString();
exceptionHandler = new google_breakpad::ExceptionHandler(tmpLocation,
nullptr,
callback,
nullptr,
google_breakpad::ExceptionHandler::HANDLER_ALL);
#endif
atexit(finishCrashHandler);
} }
[[noreturn]] void crashHandler(int signum) #else // CUTTER_ENABLE_CRASH_REPORTS
void initCrashHandler()
{ {
// As soon as Cutter crashed, crash dump is created, so core and memory state
// is not changed by all stuff with user interation going on below.
bool ok = writeMinidump();
QString err = sigNumDescription.contains(signum) ? }
sigNumDescription[signum] :
QObject::tr("undefined"); #endif // CUTTER_ENABLE_CRASH_REPORTS
void showCrashDialog(const QString &dumpFile)
{
QMessageBox mb; QMessageBox mb;
mb.setWindowTitle(QObject::tr("Cutter encountered a problem")); mb.setWindowTitle(QObject::tr("Cutter encountered a problem"));
mb.setText(QObject::tr("Cutter received a <b>%1</b> it can't handle and will close.<br/>" mb.setText(QObject::tr("Cutter received a signal it can't handle and will close.<br/>"
"Would you like to create a crash dump for bug report?" "Would you like to create a crash dump for bug report?"));
).arg(err));
mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No); mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
mb.button(QMessageBox::Yes)->setText(QObject::tr("Create a crash dump")); mb.button(QMessageBox::Yes)->setText(QObject::tr("Create a crash dump"));
mb.button(QMessageBox::No)->setText(QObject::tr("Do not report")); mb.button(QMessageBox::No)->setText(QObject::tr("Do not report"));
mb.setDefaultButton(QMessageBox::Yes); mb.setDefaultButton(QMessageBox::Yes);
bool ok = false;
int ret = mb.exec(); int ret = mb.exec();
if (ret == QMessageBox::Yes) { if (ret == QMessageBox::Yes) {
QString dumpSaveFileName; QString dumpSaveFileName;
@ -141,7 +135,6 @@ bool writeMinidump()
do { do {
placementFailCounter++; placementFailCounter++;
if (placementFailCounter == 4) { if (placementFailCounter == 4) {
ok = false;
break; break;
} }
dumpSaveFileName = QFileDialog::getSaveFileName(nullptr, dumpSaveFileName = QFileDialog::getSaveFileName(nullptr,
@ -154,9 +147,10 @@ bool writeMinidump()
QObject::tr("Dump files (*.dmp)")); QObject::tr("Dump files (*.dmp)"));
if (dumpSaveFileName.isEmpty()) { if (dumpSaveFileName.isEmpty()) {
exit(3); return;
} }
if (QFile::rename(dumpFileFullPath, dumpSaveFileName)) { if (QFile::rename(dumpFile, dumpSaveFileName)) {
ok = true;
break; break;
} }
QMessageBox::critical(nullptr, QMessageBox::critical(nullptr,
@ -187,43 +181,7 @@ bool writeMinidump()
QObject::tr("Error occured during crash dump creation.")); QObject::tr("Error occured during crash dump creation."));
} }
} else { } else {
QFile f(dumpFileFullPath); QFile f(dumpFile);
f.remove(); f.remove();
} }
_exit(3);
} }
void initCrashHandler()
{
#ifdef SIGSEGV
signal(SIGSEGV, crashHandler);
#endif // SIGSEGV
#ifdef SIGILL
signal(SIGILL, crashHandler);
#endif // SIGILL
#ifdef SIGFPE
signal(SIGFPE, crashHandler);
#endif // SIGFPE
#ifdef SIGABRT
signal(SIGABRT, crashHandler);
#endif // SIGABRT
#ifdef SIGBUS
signal(SIGBUS, crashHandler);
#endif // SIGBUS
#ifdef SIGPIPE
signal(SIGPIPE, crashHandler);
#endif // SIGPIPE
#ifdef SIGSYS
signal(SIGSYS, crashHandler);
#endif // SIGSYS
}
#else // CUTTER_ENABLE_CRASH_REPORTS
void initCrashHandler()
{
}
#endif // CUTTER_ENABLE_CRASH_REPORTS

View File

@ -1,6 +1,8 @@
#ifndef CRASH_HANDLER_H #ifndef CRASH_HANDLER_H
#define CRASH_HANDLER_H #define CRASH_HANDLER_H
#include <QString>
/** /**
* @fn void initCrashHandler() * @fn void initCrashHandler()
* *
@ -9,5 +11,6 @@
*/ */
void initCrashHandler(); void initCrashHandler();
void showCrashDialog(const QString &dumpFile);
#endif // CRASH_HANDLER_H #endif // CRASH_HANDLER_H