From c32ed5955c752ec33fed24f2caed141d18205007 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 28 Feb 2020 13:21:32 +1100 Subject: [PATCH] implemented simple crash handler --- app/CMakeLists.txt | 40 ++++++- app/common/CMakeLists.txt | 2 + app/common/crashhandler.cpp | 117 +++++++++++++++++++ app/common/crashhandler.h | 6 + app/dialog/crashhandler/crashhandler.cpp | 47 ++++++++ app/dialog/crashhandler/crashhandler.h | 19 +++ app/dialog/crashhandler/crashhandlermain.cpp | 17 +++ app/main.cpp | 4 + 8 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 app/common/crashhandler.cpp create mode 100644 app/common/crashhandler.h create mode 100644 app/dialog/crashhandler/crashhandler.cpp create mode 100644 app/dialog/crashhandler/crashhandler.h create mode 100644 app/dialog/crashhandler/crashhandlermain.cpp diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 334bed5a9..353532f36 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -108,7 +108,8 @@ target_include_directories( ${OIIO_INCLUDE_DIRS} ) -target_link_libraries(${OLIVE_TARGET} +target_link_libraries( + ${OLIVE_TARGET} PRIVATE Qt5::Core Qt5::Gui @@ -127,6 +128,14 @@ target_link_libraries(${OLIVE_TARGET} ${OIIO_LIBRARIES} ) +if (WIN32) + target_link_libraries( + ${OLIVE_TARGET} + PRIVATE + DbgHelp + ) +endif() + set(OLIVE_TS_FILES # FIXME: Empty variable ) @@ -151,3 +160,32 @@ if(DOXYGEN_FOUND) set(DOXYGEN_EXTRACT_PRIVATE "YES") doxygen_add_docs(docs ALL ${OLIVE_SOURCES}) endif() + +set(OLIVE_CRASH_TARGET "crashhandler") + +set(OLIVE_CRASH_SOURCES + dialog/crashhandler/crashhandler.h + dialog/crashhandler/crashhandler.cpp + dialog/crashhandler/crashhandlermain.cpp +) + +if (WIN32) + add_executable( + ${OLIVE_CRASH_TARGET} + WIN32 + ${OLIVE_CRASH_SOURCES} + ) +else() + add_executable( + ${OLIVE_CRASH_TARGET} + ${OLIVE_CRASH_SOURCES} + ) +endif() + +target_link_libraries( + ${OLIVE_CRASH_TARGET} + PRIVATE + Qt5::Core + Qt5::Gui + Qt5::Widgets +) diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt index 743df6778..c02c8acf2 100644 --- a/app/common/CMakeLists.txt +++ b/app/common/CMakeLists.txt @@ -22,6 +22,8 @@ set(OLIVE_SOURCES common/channellayout.h common/clamp.h common/constructors.h + common/crashhandler.h + common/crashhandler.cpp common/debug.h common/debug.cpp common/define.h diff --git a/app/common/crashhandler.cpp b/app/common/crashhandler.cpp new file mode 100644 index 000000000..8ed1579be --- /dev/null +++ b/app/common/crashhandler.cpp @@ -0,0 +1,117 @@ +#include "crashhandler.h" + +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_WINDOWS +#include +#include +#include +#include +#endif + +void crash_handler(int sig) { + QString log_path = QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation)).filePath("olive_crash"); + QFile output(log_path); + + output.open(QFile::WriteOnly); + QTextStream ostream(&output); + + ostream << "Signal: " << sig << "\n\n"; + +#if defined(Q_OS_WINDOWS) + // Use Windows stackwalk API + HANDLE process = GetCurrentProcess(); + HANDLE thread = GetCurrentThread(); + + CONTEXT context; + memset(&context, 0, sizeof(CONTEXT)); + context.ContextFlags = CONTEXT_FULL; + RtlCaptureContext(&context); + + SymInitialize(process, NULL, TRUE); + + DWORD image; + STACKFRAME64 stackframe; + ZeroMemory(&stackframe, sizeof(STACKFRAME64)); + +#ifdef _M_IX86 + image = IMAGE_FILE_MACHINE_I386; + stackframe.AddrPC.Offset = context.Eip; + stackframe.AddrPC.Mode = AddrModeFlat; + stackframe.AddrFrame.Offset = context.Ebp; + stackframe.AddrFrame.Mode = AddrModeFlat; + stackframe.AddrStack.Offset = context.Esp; + stackframe.AddrStack.Mode = AddrModeFlat; +#elif _M_X64 + image = IMAGE_FILE_MACHINE_AMD64; + stackframe.AddrPC.Offset = context.Rip; + stackframe.AddrPC.Mode = AddrModeFlat; + stackframe.AddrFrame.Offset = context.Rsp; + stackframe.AddrFrame.Mode = AddrModeFlat; + stackframe.AddrStack.Offset = context.Rsp; + stackframe.AddrStack.Mode = AddrModeFlat; +#elif _M_IA64 + image = IMAGE_FILE_MACHINE_IA64; + stackframe.AddrPC.Offset = context.StIIP; + stackframe.AddrPC.Mode = AddrModeFlat; + stackframe.AddrFrame.Offset = context.IntSp; + stackframe.AddrFrame.Mode = AddrModeFlat; + stackframe.AddrBStore.Offset = context.RsBSP; + stackframe.AddrBStore.Mode = AddrModeFlat; + stackframe.AddrStack.Offset = context.IntSp; + stackframe.AddrStack.Mode = AddrModeFlat; +#endif + + for (int i = 0; i < 50; i++) { + + BOOL result = StackWalk64( + image, process, thread, + &stackframe, &context, NULL, + SymFunctionTableAccess64, SymGetModuleBase64, NULL); + + if (!result) { break; } + + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + + DWORD64 displacement = 0; + + ostream << "[" << i << "] "; + + if (SymFromAddr(process, stackframe.AddrPC.Offset, &displacement, symbol)) { + ostream << symbol->Name; + //printf("[%i] %s\n", i, symbol->Name); + } else { + ostream << "???"; + //printf("[%i] ???\n", i); + } + + ostream << "\n"; + } + + SymCleanup(process); +#elif defined(Q_OS_LINUX) + void *array[10]; + size_t size; + + // get void*'s for all entries on the stack + size = backtrace(array, 10); + + // print out all the frames to stderr + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, STDERR_FILENO); +#endif + + output.close(); + + QProcess::startDetached(QStringLiteral("crashhandler"), {log_path}); + + exit(1); +} diff --git a/app/common/crashhandler.h b/app/common/crashhandler.h new file mode 100644 index 000000000..4044cdc5e --- /dev/null +++ b/app/common/crashhandler.h @@ -0,0 +1,6 @@ +#ifndef CRASHHANDLER_H +#define CRASHHANDLER_H + +void crash_handler(int sig); + +#endif // CRASHHANDLER_H diff --git a/app/dialog/crashhandler/crashhandler.cpp b/app/dialog/crashhandler/crashhandler.cpp new file mode 100644 index 000000000..0d1078cb3 --- /dev/null +++ b/app/dialog/crashhandler/crashhandler.cpp @@ -0,0 +1,47 @@ +#include "crashhandler.h" + +#include +#include +#include +#include +#include + +CrashHandlerDialog::CrashHandlerDialog(const char *log_file) +{ + setWindowTitle(tr("Olive")); + + QVBoxLayout* layout = new QVBoxLayout(this); + + layout->addWidget(new QLabel(tr("We're sorry, Olive has crashed. Please send the following log to the developers to " + "help resolve this."))); + + QTextEdit* edit = new QTextEdit(); + layout->addWidget(edit); + + QDialogButtonBox* buttons = new QDialogButtonBox(); + + // FIXME: Implement auto-reporting + //buttons->addButton(tr("Send Error Report"), QDialogButtonBox::AcceptRole); + //buttons->addButton(tr("Don't Send"), QDialogButtonBox::RejectRole); + buttons->addButton(QDialogButtonBox::Ok); + + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + layout->addWidget(buttons); + + QFile log(log_file); + if (log.open(QFile::ReadOnly | QFile::Text)) { + edit->setText(log.readAll()); + log.close(); + } +} + +void CrashHandlerDialog::accept() +{ + QDialog::accept(); +} + +void CrashHandlerDialog::reject() +{ + QDialog::reject(); +} diff --git a/app/dialog/crashhandler/crashhandler.h b/app/dialog/crashhandler/crashhandler.h new file mode 100644 index 000000000..80aed6143 --- /dev/null +++ b/app/dialog/crashhandler/crashhandler.h @@ -0,0 +1,19 @@ +#ifndef CRASHHANDLERDIALOG_H +#define CRASHHANDLERDIALOG_H + +#include + +class CrashHandlerDialog : public QDialog +{ + Q_OBJECT +public: + CrashHandlerDialog(const char* log_file); + +public slots: + virtual void accept() override; + + virtual void reject() override; + +}; + +#endif // CRASHHANDLERDIALOG_H diff --git a/app/dialog/crashhandler/crashhandlermain.cpp b/app/dialog/crashhandler/crashhandlermain.cpp new file mode 100644 index 000000000..945835d6d --- /dev/null +++ b/app/dialog/crashhandler/crashhandlermain.cpp @@ -0,0 +1,17 @@ +#include "crashhandler.h" + +#include + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + return 1; + } + + QApplication a(argc, argv); + + CrashHandlerDialog chd(argv[1]); + chd.open(); + + return a.exec(); +} diff --git a/app/main.cpp b/app/main.cpp index e4e7491d6..869e10ad7 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -35,11 +35,15 @@ extern "C" { #include #include "core.h" +#include "common/crashhandler.h" #include "common/debug.h" int main(int argc, char *argv[]) { av_log_set_level(AV_LOG_QUIET); + signal(SIGSEGV, crash_handler); + signal(SIGABRT, crash_handler); + // Set OpenGL display profile (3.2 Core) QSurfaceFormat format; format.setVersion(3, 2);