crashpad: updated crash handler

This commit is contained in:
itsmattkc
2020-09-22 18:42:51 +10:00
parent 871bc08c12
commit cd47676043
11 changed files with 298 additions and 285 deletions
+148 -33
View File
@@ -20,66 +20,181 @@
#include "crashhandler.h"
#include <QApplication>
#include <QDir>
#include <QFile>
#include <QFontDatabase>
#include <QLabel>
#include <QDialogButtonBox>
#include <QHttpMultiPart>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QProcess>
#include <QScrollBar>
#include <QTextEdit>
#include <QThread>
#include <QTimer>
#include <QVBoxLayout>
#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<int, QProcess::ExitStatus>::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<crashpad::CrashReportDatabase> database = crashpad::CrashReportDatabase::Initialize(base::FilePath(QSTRING_TO_BASE_STRING(QString(report_dir_))));
std::vector<crashpad::CrashReportDatabase::Report> 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<QProcess*>(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