From cd47676043e45ab8a11e442f84efcfd630690219 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 22 Sep 2020 16:27:03 +1000 Subject: [PATCH] crashpad: updated crash handler --- .github/workflows/ci.yml | 3 + app/CMakeLists.txt | 88 +++++---- app/common/CMakeLists.txt | 5 +- app/common/crashhandler.cpp | 150 --------------- app/common/crashpadinterface.cpp | 81 ++++---- app/common/crashpadinterface.h | 1 - .../{crashhandler.h => crashpadutils.h} | 26 ++- app/dialog/crashhandler/crashhandler.cpp | 181 ++++++++++++++---- app/dialog/crashhandler/crashhandler.h | 40 +++- app/dialog/crashhandler/crashhandlermain.cpp | 4 +- cmake/FindGoogleCrashpad.cmake | 4 + 11 files changed, 298 insertions(+), 285 deletions(-) delete mode 100644 app/common/crashhandler.cpp rename app/common/{crashhandler.h => crashpadutils.h} (50%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d8c1a4b4..b4756b7f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -139,10 +139,13 @@ jobs: then mkdir olive-editor cp app/olive-editor.exe olive-editor + cp app/olive-crashhandler.exe olive-editor cp app/olive-editor.pdb olive-editor + windeployqt olive-editor/olive-crashhandler.exe windeployqt olive-editor/olive-editor.exe cp $(cygpath $GITHUB_WORKSPACE)/bin/*.dll olive-editor cp $(cygpath $GITHUB_WORKSPACE)/out/Default/crashpad_handler.exe olive-editor + cp $(cygpath $GITHUB_WORKSPACE)/minidump_stackwalk/* olive-editor elif [ "$MATRIX_OS" == "macos-latest" ] then export BUNDLE_NAME=Olive.app diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 714a5e01d..b0bfd881f 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -183,6 +183,54 @@ if (GoogleCrashpad_FOUND) PRIVATE ${CRASHPAD_LIBRARIES} ) + + set(OLIVE_CRASH_TARGET "olive-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_include_directories( + ${OLIVE_CRASH_TARGET} + PRIVATE + ${CRASHPAD_INCLUDE_DIRS} + ) + + target_link_libraries( + ${OLIVE_CRASH_TARGET} + PRIVATE + Qt5::Core + Qt5::Gui + Qt5::Widgets + Qt5::Network + ${CRASHPAD_LIBRARIES} + ) + + if(UNIX AND NOT APPLE) + install(TARGETS ${OLIVE_TARGET} ${OLIVE_CRASH_TARGET} RUNTIME DESTINATION bin) + endif() + + if(APPLE) + # Move crash handler program inside Mac app bundle + add_custom_command(TARGET ${OLIVE_CRASH_TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${OLIVE_CRASH_TARGET} $ + ) + endif() endif() # Set compiler definitions @@ -207,43 +255,3 @@ if(DOXYGEN_FOUND) set(DOXYGEN_EXTRACT_PRIVATE "YES") doxygen_add_docs(docs ALL ${OLIVE_SOURCES}) endif() - -set(OLIVE_CRASH_TARGET "olive-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 -) - -if(UNIX AND NOT APPLE) - install(TARGETS ${OLIVE_TARGET} ${OLIVE_CRASH_TARGET} RUNTIME DESTINATION bin) -endif() - -if(APPLE) - # Move crash handler program inside Mac app bundle - add_custom_command(TARGET ${OLIVE_CRASH_TARGET} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${OLIVE_CRASH_TARGET} $ - ) -endif() diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt index ee07f43c4..99521453c 100644 --- a/app/common/CMakeLists.txt +++ b/app/common/CMakeLists.txt @@ -23,10 +23,9 @@ set(OLIVE_SOURCES common/clamp.h common/commandlineparser.h common/commandlineparser.cpp - common/crashhandler.h - common/crashhandler.cpp - common/crashpadinterface.cpp common/crashpadinterface.h + common/crashpadinterface.cpp + common/crashpadutils.h common/debug.h common/debug.cpp common/define.h diff --git a/app/common/crashhandler.cpp b/app/common/crashhandler.cpp deleted file mode 100644 index 469e45233..000000000 --- a/app/common/crashhandler.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - 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 3 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, see . - -***/ - -#include "crashhandler.h" - -#include -#include -#include -#include -#include -#include -#include - -#if defined(Q_OS_WINDOWS) -#include -#include -#include -#include -#elif defined(Q_OS_MAC) || defined(Q_OS_LINUX) -#include -#endif - -#include "common/filefunctions.h" - -OLIVE_NAMESPACE_ENTER - -void crash_handler(int sig) -{ - QString log_path = QDir(FileFunctions::GetTempFilePath()).filePath(QStringLiteral("olive_crash")); - QFile output(log_path); - - output.open(QFile::WriteOnly); - QTextStream ostream(&output); - -#ifdef GITHASH - ostream << "Version: " << GITHASH << "\n"; -#endif - 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_MAC) || 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 - backtrace_symbols_fd(array, size, output.handle()); -#endif - - output.close(); - - QString crash_handler_exe = QDir(qApp->applicationDirPath()).filePath(QStringLiteral("olive-crashhandler")); - QProcess::startDetached(crash_handler_exe, {log_path}); - - exit(1); -} - -OLIVE_NAMESPACE_EXIT diff --git a/app/common/crashpadinterface.cpp b/app/common/crashpadinterface.cpp index 770e8a84a..393cba4c6 100644 --- a/app/common/crashpadinterface.cpp +++ b/app/common/crashpadinterface.cpp @@ -23,75 +23,67 @@ #ifdef USE_CRASHPAD #include +#include #include #include #include +#include +#include "crashpadutils.h" #include "filefunctions.h" -#ifdef Q_OS_WINDOWS +#ifdef OS_WIN #include #endif -// Copied from base::FilePath to match its macro -#if defined(OS_POSIX) -// On most platforms, native pathnames are char arrays, and the encoding -// may or may not be specified. On Mac OS X, native pathnames are encoded -// in UTF-8. -#define TO_BASE_STRING_TYPE(x) x.toStdString() -#elif defined(OS_WIN) -// On Windows, for Unicode-aware applications, native pathnames are wchar_t -// arrays encoded in UTF-16. -#define TO_BASE_STRING_TYPE(x) x.toStdWString() -#endif // OS_WIN - crashpad::CrashpadClient *client; -bool ShowCrashConfirmation() +QString GenerateReportPath() { - QString msg = QCoreApplication::translate("CrashReport", - "We're sorry, Olive has crashed. " - "Would you like to send an error report to " - "help developers fix this issue?\n\n" - "Crash reports are anonymous and only send " - "non-specific details about your computer and" - "how the crash occurred."); - - return (QMessageBox::critical(nullptr, - QString(), - msg, - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes); + return QDir(OLIVE_NAMESPACE::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("reports")); } -#ifdef Q_OS_WINDOWS +base::FilePath GenerateReportPathForCrashpad() +{ + return base::FilePath(QSTRING_TO_BASE_STRING(GenerateReportPath())); +} + +#if defined(OS_WIN) LONG WINAPI Win32ExceptionHandler(_EXCEPTION_POINTERS *ExceptionInfo) { - if (ShowCrashConfirmation()) { - client->DumpAndCrash(ExceptionInfo); - } + QString crash_handler_exe = QDir(qApp->applicationDirPath()).filePath(QStringLiteral("olive-crashhandler")); + QProcess::startDetached(crash_handler_exe, {GenerateReportPath(), QString::number(QDateTime::currentSecsSinceEpoch())}); + + client->DumpAndCrash(ExceptionInfo); return EXCEPTION_CONTINUE_SEARCH; } +#elif defined(OS_LINUX) +bool LinuxExceptionHandler(int, siginfo_t*, ucontext_t*) +{ + QString crash_handler_exe = QDir(qApp->applicationDirPath()).filePath(QStringLiteral("olive-crashhandler")); + QProcess::startDetached(crash_handler_exe, {GenerateReportPath(), QString::number(QDateTime::currentSecsSinceEpoch())}); + + // Returning false signals to Crashpad to proceed with its own exception handling + return false; +} #endif bool InitializeCrashpad() { QString exe_dir = QCoreApplication::applicationDirPath(); +#ifdef OS_WIN + base::FilePath handler(QSTRING_TO_BASE_STRING(QDir(exe_dir).filePath(QStringLiteral("crashpad_handler.exe")))); +#else // FIXME: On Linux, probably should put this in a subdir so that it doesn't conflict with // anything else in /usr/bin - -#ifdef Q_OS_WINDOWS - base::FilePath handler(TO_BASE_STRING_TYPE(QDir(exe_dir).filePath(QStringLiteral("crashpad_handler.exe")))); -#else - base::FilePath handler(TO_BASE_STRING_TYPE(QDir(exe_dir).filePath(QStringLiteral("crashpad_handler")))); + base::FilePath handler(QSTRING_TO_BASE_STRING(QDir(exe_dir).filePath(QStringLiteral("crashpad_handler")))); #endif - base::FilePath reports_dir(TO_BASE_STRING_TYPE(QDir(OLIVE_NAMESPACE::FileFunctions::GetTempFilePath()).filePath("reports"))); + base::FilePath reports_dir = GenerateReportPathForCrashpad(); - base::FilePath metrics_dir(TO_BASE_STRING_TYPE(QDir(OLIVE_NAMESPACE::FileFunctions::GetTempFilePath()).filePath("metrics"))); - - std::string url = "https://olivevideoeditor.org/crashpad/report.php"; + base::FilePath metrics_dir(QSTRING_TO_BASE_STRING(QDir(OLIVE_NAMESPACE::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("metrics")))); // Metadata that will be posted to the server with the crash report map std::map annotations; @@ -105,19 +97,22 @@ bool InitializeCrashpad() std::unique_ptr database = crashpad::CrashReportDatabase::Initialize(reports_dir); if (database == NULL) return false; - // Enable automated crash uploads + // Disable automated crash uploads crashpad::Settings *settings = database->GetSettings(); if (settings == NULL) return false; - settings->SetUploadsEnabled(true); + settings->SetUploadsEnabled(false); // Start crash handler client = new crashpad::CrashpadClient(); bool status = client->StartHandler(handler, reports_dir, metrics_dir, - url, annotations, arguments, true, true); + "https://olivevideoeditor.org/crashpad/report.php", + annotations, arguments, true, true); // Override Crashpad exception filter with our own -#ifdef Q_OS_WINDOWS +#if defined(OS_WIN) SetUnhandledExceptionFilter(Win32ExceptionHandler); +#elif defined(OS_LINUX) + crashpad::CrashpadClient::SetFirstChanceExceptionHandler(LinuxExceptionHandler); #endif return status; diff --git a/app/common/crashpadinterface.h b/app/common/crashpadinterface.h index 26bd17b65..2388ece96 100644 --- a/app/common/crashpadinterface.h +++ b/app/common/crashpadinterface.h @@ -23,7 +23,6 @@ #ifdef USE_CRASHPAD -#include #include #include diff --git a/app/common/crashhandler.h b/app/common/crashpadutils.h similarity index 50% rename from app/common/crashhandler.h rename to app/common/crashpadutils.h index 10ed30627..a2a36e49d 100644 --- a/app/common/crashhandler.h +++ b/app/common/crashpadutils.h @@ -18,15 +18,23 @@ ***/ -#ifndef CRASHHANDLER_H -#define CRASHHANDLER_H +#ifndef CRASHPADUTILS_H +#define CRASHPADUTILS_H -#include "common/define.h" +#include -OLIVE_NAMESPACE_ENTER +// Copied from base::FilePath to match its macro +#if defined(OS_POSIX) +// On most platforms, native pathnames are char arrays, and the encoding +// may or may not be specified. On Mac OS X, native pathnames are encoded +// in UTF-8. +#define QSTRING_TO_BASE_STRING(x) x.toStdString() +#define BASE_STRING_TO_QSTRING(x) QString::fromStdString(x) +#elif defined(OS_WIN) +// On Windows, for Unicode-aware applications, native pathnames are wchar_t +// arrays encoded in UTF-16. +#define QSTRING_TO_BASE_STRING(x) x.toStdWString() +#define BASE_STRING_TO_QSTRING(x) QString::fromStdWString(x) +#endif // OS_WIN -void crash_handler(int sig); - -OLIVE_NAMESPACE_EXIT - -#endif // CRASHHANDLER_H +#endif // CRASHPADUTILS_H diff --git a/app/dialog/crashhandler/crashhandler.cpp b/app/dialog/crashhandler/crashhandler.cpp index 7645eb5f9..e6120a5d9 100644 --- a/app/dialog/crashhandler/crashhandler.cpp +++ b/app/dialog/crashhandler/crashhandler.cpp @@ -20,66 +20,181 @@ #include "crashhandler.h" +#include +#include #include +#include #include -#include +#include +#include +#include +#include #include -#include +#include +#include #include +#include "common/crashpadutils.h" + OLIVE_NAMESPACE_ENTER -CrashHandlerDialog::CrashHandlerDialog(const char *log_file) +CrashHandlerDialog::CrashHandlerDialog(const char *report_dir, const char* crash_time) { setWindowTitle(tr("Olive")); + crash_time_ = QString(crash_time).toULongLong(); + report_dir_ = report_dir; 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."))); + layout->addWidget(new QLabel(tr("We're sorry, Olive has crashed. Please help us fix it by " + "sending an error report."))); - QTextEdit* edit = new QTextEdit(); - edit->setReadOnly(true); - layout->addWidget(edit); + summary_edit_ = new QTextEdit(); + summary_edit_->setPlaceholderText(tr("Describe what you were doing in as much detail as " + "possible. If you can, provide steps to reproduce this crash.")); - edit->append(QStringLiteral("Build Environment: %1 (%2)").arg(QSysInfo::buildCpuArchitecture(), QSysInfo::buildAbi())); - edit->append(QStringLiteral("Run Environment: %1").arg(QSysInfo::currentCpuArchitecture())); - edit->append(QStringLiteral("Kernel: %1 %2").arg(QSysInfo::kernelType(), QSysInfo::kernelVersion())); - edit->append(QStringLiteral("System: %1 (%2 %3)").arg(QSysInfo::prettyProductName(), QSysInfo::productType(), QSysInfo::productVersion())); - edit->append(QString()); + layout->addWidget(summary_edit_); - QDialogButtonBox* buttons = new QDialogButtonBox(); + layout->addWidget(new QLabel(tr("Crash Report:"))); - // FIXME: Implement auto-reporting - //buttons->addButton(tr("Send Error Report"), QDialogButtonBox::AcceptRole); - //buttons->addButton(tr("Don't Send"), QDialogButtonBox::RejectRole); - buttons->addButton(QDialogButtonBox::Ok); + crash_report_ = new QTextEdit(); + crash_report_->setReadOnly(true); + crash_report_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + layout->addWidget(crash_report_); - connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); - layout->addWidget(buttons); + QHBoxLayout* btn_layout = new QHBoxLayout(); + btn_layout->setMargin(0); + btn_layout->addStretch(); - QFile log(log_file); - if (log.open(QFile::ReadOnly | QFile::Text)) { - edit->append(log.readAll()); + send_report_btn_ = new QPushButton(tr("Send Error Report")); + connect(send_report_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::SendErrorReport); + btn_layout->addWidget(send_report_btn_); - QMetaObject::invokeMethod(edit->verticalScrollBar(), - "setValue", - Qt::QueuedConnection, - Q_ARG(int, 0)); + dont_send_btn_ = new QPushButton(tr("Don't Send")); + connect(dont_send_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::reject); + btn_layout->addWidget(dont_send_btn_); - log.close(); + layout->addLayout(btn_layout); + + crash_report_->setEnabled(false); + send_report_btn_->setEnabled(false); + + crash_report_->setText(tr("Waiting for crash report to be generated...")); + + AttemptToFindReport(); +} + +void CrashHandlerDialog::SetGUIObjectsEnabled(bool e) +{ + summary_edit_->setEnabled(e); + crash_report_->setEnabled(e); + send_report_btn_->setEnabled(e); + dont_send_btn_->setEnabled(e); +} + +void CrashHandlerDialog::GenerateReport() +{ + QProcess* p = new QProcess(); + + connect(p, QOverload::of(&QProcess::finished), + this, &CrashHandlerDialog::ReadProcessFinished); + connect(p, &QProcess::readyReadStandardOutput, this, &CrashHandlerDialog::ReadProcessHasData); + + QString stackwalk_filename; + +#if defined(OS_WIN) + stackwalk_filename = QStringLiteral("minidump_stackwalk.exe"); +#else + stackwalk_filename = QStringLiteral("minidump_stackwalk"); +#endif + + QString stackwalk_bin = QDir(qApp->applicationDirPath()).filePath(stackwalk_filename); + p->start(stackwalk_bin, {report_filename_}); + crash_report_->setText(QStringLiteral("Trying to run: %1").arg(stackwalk_bin)); +} + +void CrashHandlerDialog::ReplyFinished(QNetworkReply* reply) +{ + if (reply->error() == QNetworkReply::NoError) { + // Close dialog + QDialog::accept(); + } else { + QMessageBox::critical(this, tr("Upload Failed"), + tr("Failed to send error report. Please try again later."), + QMessageBox::Ok); + SetGUIObjectsEnabled(true); } } -void CrashHandlerDialog::accept() +void CrashHandlerDialog::AttemptToFindReport() { - QDialog::accept(); + // Retrieve reports from Crashpad database + std::unique_ptr database = crashpad::CrashReportDatabase::Initialize(base::FilePath(QSTRING_TO_BASE_STRING(QString(report_dir_)))); + std::vector reports; + database->GetCompletedReports(&reports); + + // Find report that was made after the crash time + foreach (const crashpad::CrashReportDatabase::Report& report, reports) { + if (report.creation_time >= crash_time_) { + report_filename_ = BASE_STRING_TO_QSTRING(report.file_path.value()); + break; + } + } + + // If we found it, use it, otherwise wait a second and try again + if (report_filename_.isEmpty()) { + // Couldn't find report, try again in one second + QTimer::singleShot(500, this, &CrashHandlerDialog::AttemptToFindReport); + } else { + GenerateReport(); + } } -void CrashHandlerDialog::reject() +void CrashHandlerDialog::ReadProcessHasData() { - QDialog::reject(); + report_data_.append(static_cast(sender())->readAllStandardOutput()); +} + +void CrashHandlerDialog::ReadProcessFinished() +{ + SetGUIObjectsEnabled(true); + crash_report_->setText(report_data_); + delete sender(); +} + +void CrashHandlerDialog::SendErrorReport() +{ + SetGUIObjectsEnabled(false); + + QNetworkAccessManager* manager = new QNetworkAccessManager(); + connect(manager, &QNetworkAccessManager::finished, this, &CrashHandlerDialog::ReplyFinished); + + QNetworkRequest request; + request.setSslConfiguration(QSslConfiguration::defaultConfiguration()); + request.setUrl(QStringLiteral("https://olivevideoeditor.org/crashpad/report.php")); + + // Create HTTP form + QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + // Create description section + QHttpPart desc_part; + desc_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain")); + desc_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"description\"")); + desc_part.setBody(summary_edit_->toPlainText().toUtf8()); + multipart->append(desc_part); + + // Create file section + QHttpPart file_part; + file_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream")); + file_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"upload_file_minidump\"; filename=\"%1\"") + .arg(QFileInfo(report_filename_).fileName())); + QFile* file = new QFile(report_filename_); + file->open(QFile::ReadOnly); + file_part.setBodyDevice(file); + file->setParent(multipart); // Delete file with multipart + multipart->append(file_part); + + manager->post(request, multipart); } OLIVE_NAMESPACE_EXIT diff --git a/app/dialog/crashhandler/crashhandler.h b/app/dialog/crashhandler/crashhandler.h index 485f1d543..7ec91669b 100644 --- a/app/dialog/crashhandler/crashhandler.h +++ b/app/dialog/crashhandler/crashhandler.h @@ -21,7 +21,12 @@ #ifndef CRASHHANDLERDIALOG_H #define CRASHHANDLERDIALOG_H +#include #include +#include +#include +#include +#include #include "common/define.h" @@ -31,12 +36,39 @@ class CrashHandlerDialog : public QDialog { Q_OBJECT public: - CrashHandlerDialog(const char* log_file); + CrashHandlerDialog(const char* report_dir, const char* crash_time); -public slots: - virtual void accept() override; +private: + void SetGUIObjectsEnabled(bool e); - virtual void reject() override; + void GenerateReport(); + + QTextEdit* summary_edit_; + + QTextEdit* crash_report_; + + QPushButton* send_report_btn_; + + QPushButton* dont_send_btn_; + + QString report_filename_; + + time_t crash_time_; + + QString report_dir_; + + QByteArray report_data_; + +private slots: + void ReplyFinished(QNetworkReply *reply); + + void AttemptToFindReport(); + + void ReadProcessHasData(); + + void ReadProcessFinished(); + + void SendErrorReport(); }; diff --git a/app/dialog/crashhandler/crashhandlermain.cpp b/app/dialog/crashhandler/crashhandlermain.cpp index 88032fff4..d0fe76368 100644 --- a/app/dialog/crashhandler/crashhandlermain.cpp +++ b/app/dialog/crashhandler/crashhandlermain.cpp @@ -24,13 +24,13 @@ int main(int argc, char *argv[]) { - if (argc < 2) { + if (argc < 3) { return 1; } QApplication a(argc, argv); - OLIVE_NAMESPACE::CrashHandlerDialog chd(argv[1]); + OLIVE_NAMESPACE::CrashHandlerDialog chd(argv[1], argv[2]); chd.open(); return a.exec(); diff --git a/cmake/FindGoogleCrashpad.cmake b/cmake/FindGoogleCrashpad.cmake index 60ef6b849..17474a407 100644 --- a/cmake/FindGoogleCrashpad.cmake +++ b/cmake/FindGoogleCrashpad.cmake @@ -79,6 +79,10 @@ foreach (COMPONENT ${_crashpad_components}) list(APPEND CRASHPAD_LIBRARIES ${CRASHPAD_${UPPERCOMPONENT}_LIB}) endforeach() +if (UNIX AND NOT APPLE) + list(APPEND CRASHPAD_LIBRARIES ${CMAKE_DL_LIBS} Threads::Threads) +endif() + find_package_handle_standard_args(GoogleCrashpad REQUIRED_VARS CRASHPAD_LIBRARIES