File indexing completed on 2024-04-28 09:21:01
0001 /******************************************************************* 0002 * reportinterface.cpp 0003 * SPDX-FileCopyrightText: 2009, 2010, 2011 Dario Andres Rodriguez <andresbajotierra@gmail.com> 0004 * SPDX-FileCopyrightText: 2009 George Kiagiadakis <gkiagia@users.sourceforge.net> 0005 * SPDX-FileCopyrightText: 2021-2022 Harald Sitter <sitter@kde.org> 0006 * 0007 * SPDX-License-Identifier: GPL-2.0-or-later 0008 * 0009 ******************************************************************/ 0010 0011 #include "reportinterface.h" 0012 0013 #include <chrono> 0014 0015 #include <QGuiApplication> 0016 0017 #include <KIO/TransferJob> 0018 #include <KLocalizedString> 0019 0020 #include "backtracegenerator.h" 0021 #include "bugzillalib.h" 0022 #include "config-drkonqi.h" 0023 #include "crashedapplication.h" 0024 #include "debuggermanager.h" 0025 #include "drkonqi.h" 0026 #include "drkonqi_debug.h" 0027 #include "parser/backtraceparser.h" 0028 #include "productmapping.h" 0029 #include "sentryconnection.h" 0030 #include "settings.h" 0031 #include "systeminformation.h" 0032 0033 using namespace std::chrono_literals; 0034 using namespace Qt::StringLiterals; 0035 0036 // Max size a report may have. This is enforced in bugzilla, hardcoded, and 0037 // cannot be queried through the API, so handle this client-side in a hardcoded 0038 // fashion as well. 0039 static const int s_maxReportSize = 65535; 0040 0041 ReportInterface::ReportInterface(QObject *parent) 0042 : QObject(parent) 0043 , m_sentryPostbox(DrKonqi::crashedApplication()->fakeExecutableBaseName(), std::make_shared<SentryNetworkConnection>()) 0044 { 0045 m_bugzillaManager = new BugzillaManager(KDE_BUGZILLA_URL, this); 0046 0047 m_productMapping = new ProductMapping(DrKonqi::crashedApplication(), m_bugzillaManager, this); 0048 0049 // Information the user can provide about the crash 0050 m_userRememberCrashSituation = false; 0051 m_reproducible = ReproducibleUnsure; 0052 m_provideActionsApplicationDesktop = false; 0053 m_provideUnusualBehavior = false; 0054 m_provideApplicationConfigurationDetails = false; 0055 0056 // Do not attach the bug report to any other existent report (create a new one) 0057 m_attachToBugNumber = 0; 0058 0059 connect(&m_sentryPostbox, &SentryPostbox::hasDeliveredChanged, this, [this] { 0060 maybeDone(); 0061 }); 0062 0063 m_sentryStartTimer.setInterval(5s); 0064 m_sentryStartTimer.setSingleShot(true); 0065 connect(&m_sentryStartTimer, &QTimer::timeout, this, &ReportInterface::trySentry); 0066 connect(Settings::self(), &Settings::SentryChanged, &m_sentryStartTimer, QOverload<>::of(&QTimer::start)); 0067 trySentry(); 0068 } 0069 0070 void ReportInterface::setBugAwarenessPageData(bool rememberSituation, Reproducible reproducible, bool actions, bool unusual, bool configuration) 0071 { 0072 // Save the information the user can provide about the crash from the assistant page 0073 m_userRememberCrashSituation = rememberSituation; 0074 m_reproducible = reproducible; 0075 m_provideActionsApplicationDesktop = actions; 0076 m_provideUnusualBehavior = unusual; 0077 m_provideApplicationConfigurationDetails = configuration; 0078 } 0079 0080 bool ReportInterface::isBugAwarenessPageDataUseful() const 0081 { 0082 // Determine if the assistant should proceed, considering the amount of information 0083 // the user can provide 0084 int rating = selectedOptionsRating(); 0085 0086 // Minimum information required even for a good backtrace. 0087 bool useful = m_userRememberCrashSituation && (rating >= 2 || (m_reproducible == ReproducibleSometimes || m_reproducible == ReproducibleEverytime)); 0088 return useful; 0089 } 0090 0091 int ReportInterface::selectedOptionsRating() const 0092 { 0093 // Check how many information the user can provide and generate a rating 0094 int rating = 0; 0095 if (m_provideActionsApplicationDesktop) { 0096 rating += 3; 0097 } 0098 if (m_provideApplicationConfigurationDetails) { 0099 rating += 2; 0100 } 0101 if (m_provideUnusualBehavior) { 0102 rating += 1; 0103 } 0104 return rating; 0105 } 0106 0107 QString ReportInterface::backtrace() const 0108 { 0109 return m_backtrace; 0110 } 0111 0112 void ReportInterface::setBacktrace(const QString &backtrace) 0113 { 0114 m_backtrace = backtrace; 0115 Q_EMIT backtraceChanged(); 0116 } 0117 0118 QString ReportInterface::title() const 0119 { 0120 return m_reportTitle; 0121 } 0122 0123 void ReportInterface::setTitle(const QString &text) 0124 { 0125 m_reportTitle = text; 0126 Q_EMIT titleChanged(); 0127 } 0128 0129 void ReportInterface::setDetailText(const QString &text) 0130 { 0131 m_reportDetailText = text; 0132 Q_EMIT detailTextChanged(); 0133 } 0134 0135 QString ReportInterface::generateReportFullText(DrKonqiStamp stamp, Backtrace inlineBacktrace) const 0136 { 0137 // Note: no translations should be done in this function's strings 0138 0139 const CrashedApplication *crashedApp = DrKonqi::crashedApplication(); 0140 const SystemInformation *sysInfo = DrKonqi::systemInformation(); 0141 0142 QString report; 0143 0144 // Program name and versions 0145 report.append(QStringLiteral("Application: %1 (%2)\n").arg(crashedApp->fakeExecutableBaseName(), crashedApp->version())); 0146 if (sysInfo->compiledSources()) { 0147 report.append(QStringLiteral(" (Compiled from sources)\n")); 0148 } else { 0149 report.append(QLatin1Char('\n')); 0150 } 0151 report.append(QStringLiteral("Qt Version: %1\n").arg(sysInfo->qtVersion())); 0152 report.append(QStringLiteral("Frameworks Version: %1\n").arg(sysInfo->frameworksVersion())); 0153 0154 report.append(QStringLiteral("Operating System: %1\n").arg(sysInfo->operatingSystem())); 0155 report.append(QStringLiteral("Windowing System: %1\n").arg(sysInfo->windowSystem())); 0156 0157 // LSB output or manually selected distro 0158 if (!sysInfo->distributionPrettyName().isEmpty()) { 0159 report.append(QStringLiteral("Distribution: %1\n").arg(sysInfo->distributionPrettyName())); 0160 } else if (!sysInfo->bugzillaPlatform().isEmpty() && sysInfo->bugzillaPlatform() != QLatin1String("unspecified")) { 0161 report.append(QStringLiteral("Distribution (Platform): %1\n").arg(sysInfo->bugzillaPlatform())); 0162 } 0163 0164 report.append(QStringLiteral("DrKonqi: %1 [%2]\n").arg(QString::fromLatin1(PROJECT_VERSION), DrKonqi::backendClassName())); 0165 report.append(QLatin1Char('\n')); 0166 0167 // Details of the crash situation 0168 if (isBugAwarenessPageDataUseful()) { 0169 report.append(QStringLiteral("-- Information about the crash:\n")); 0170 if (!m_reportDetailText.isEmpty()) { 0171 report.append(m_reportDetailText.trimmed()); 0172 } else { 0173 // If the user manual reports this crash, he/she should know what to put in here. 0174 // This message is the only one translated in this function 0175 report.append(xi18nc("@info/plain", 0176 "<placeholder>In detail, tell us what you were doing " 0177 " when the application crashed.</placeholder>")); 0178 } 0179 report.append(QLatin1String("\n\n")); 0180 } 0181 0182 // Crash reproducibility 0183 switch (m_reproducible) { 0184 case ReproducibleUnsure: 0185 report.append(QStringLiteral("The reporter is unsure if this crash is reproducible.\n\n")); 0186 break; 0187 case ReproducibleNever: 0188 report.append(QStringLiteral("The crash does not seem to be reproducible.\n\n")); 0189 break; 0190 case ReproducibleSometimes: 0191 report.append(QStringLiteral("The crash can be reproduced sometimes.\n\n")); 0192 break; 0193 case ReproducibleEverytime: 0194 report.append(QStringLiteral("The crash can be reproduced every time.\n\n")); 0195 break; 0196 } 0197 0198 // Backtrace 0199 switch (inlineBacktrace) { 0200 case Backtrace::Complete: 0201 report.append(QStringLiteral("-- Backtrace:\n")); 0202 break; 0203 case Backtrace::Reduced: 0204 report.append(QStringLiteral("-- Backtrace (Reduced):\n")); 0205 break; 0206 case Backtrace::Exclude: 0207 report.append(QStringLiteral("The backtrace was excluded and likely attached as a file.\n")); 0208 break; 0209 } 0210 if (!m_backtrace.isEmpty()) { 0211 switch (inlineBacktrace) { 0212 case Backtrace::Complete: 0213 report.append(m_backtrace.trimmed() + QLatin1Char('\n')); 0214 break; 0215 case Backtrace::Reduced: 0216 report.append(DrKonqi::debuggerManager()->backtraceGenerator()->parser()->simplifiedBacktrace() + QLatin1Char('\n')); 0217 break; 0218 case Backtrace::Exclude: 0219 report.append(QStringLiteral("The backtrace is attached as a comment due to length constraints\n")); 0220 break; 0221 } 0222 } else { 0223 report.append(QStringLiteral("A useful backtrace could not be generated\n")); 0224 } 0225 0226 switch (stamp) { 0227 case DrKonqiStamp::Include: { 0228 report.append(QLatin1String("\nReported using DrKonqi")); 0229 const QString product = m_productMapping->bugzillaProduct(); 0230 const QString originalProduct = m_productMapping->bugzillaProductOriginal(); 0231 if (!originalProduct.isEmpty()) { 0232 report.append( 0233 QStringLiteral( 0234 "\nThis report was filed against '%1' because the product '%2' could not be located in Bugzilla. Add it to drkonqi's mappings file!") 0235 .arg(product, originalProduct)); 0236 } 0237 break; 0238 } 0239 case DrKonqiStamp::Exclude: 0240 break; 0241 } 0242 0243 return report; 0244 } 0245 0246 QString ReportInterface::generateAttachmentComment() const 0247 { 0248 // Note: no translations should be done in this function's strings 0249 0250 const CrashedApplication *crashedApp = DrKonqi::crashedApplication(); 0251 const SystemInformation *sysInfo = DrKonqi::systemInformation(); 0252 0253 QString comment; 0254 0255 // Program name and versions 0256 comment.append(QStringLiteral("%1 (%2) using Qt %4\n\n").arg(crashedApp->fakeExecutableBaseName(), crashedApp->version(), sysInfo->qtVersion())); 0257 0258 // Details of the crash situation 0259 if (isBugAwarenessPageDataUseful()) { 0260 comment.append(QStringLiteral("%1\n\n").arg(m_reportDetailText.trimmed())); 0261 } 0262 0263 // Backtrace (only 6 lines) 0264 comment.append(QStringLiteral("-- Backtrace (Reduced):\n")); 0265 QString reducedBacktrace = DrKonqi::debuggerManager()->backtraceGenerator()->parser()->simplifiedBacktrace(); 0266 comment.append(reducedBacktrace.trimmed()); 0267 0268 return comment; 0269 } 0270 0271 Bugzilla::NewBug ReportInterface::newBugReportTemplate() const 0272 { 0273 const SystemInformation *sysInfo = DrKonqi::systemInformation(); 0274 0275 Bugzilla::NewBug bug; 0276 bug.product = m_productMapping->bugzillaProduct(); 0277 bug.component = m_productMapping->bugzillaComponent(); 0278 bug.version = m_productMapping->bugzillaVersion(); 0279 bug.op_sys = sysInfo->bugzillaOperatingSystem(); 0280 if (sysInfo->compiledSources()) { 0281 bug.platform = QLatin1String("Compiled Sources"); 0282 } else { 0283 bug.platform = sysInfo->bugzillaPlatform(); 0284 } 0285 bug.keywords = QStringList{QStringLiteral("drkonqi")}; 0286 bug.priority = QLatin1String("NOR"); 0287 bug.severity = QLatin1String("crash"); 0288 bug.summary = m_reportTitle; 0289 0290 return bug; 0291 } 0292 0293 QByteArray journalPriorityToSentryLevel(const QByteArray &priorityBytes) 0294 { 0295 bool ok = false; 0296 auto priority = priorityBytes.toInt(&ok); 0297 Q_ASSERT(ok); 0298 if (!ok) { 0299 return "info"_ba; 0300 } 0301 // https://www.freedesktop.org/software/systemd/man/latest/systemd.journal-fields.html#PRIORITY= 0302 switch (priority) { 0303 case 0: 0304 case 1: 0305 case 2: 0306 return "fatal"_ba; 0307 case 3: 0308 return "error"_ba; 0309 case 4: 0310 return "warning"_ba; 0311 case 5: 0312 case 6: 0313 return "info"_ba; 0314 case 7: 0315 return "debug"_ba; 0316 }; 0317 return "info"_ba; 0318 } 0319 0320 void ReportInterface::prepareEventPayload() 0321 { 0322 auto eventPayload = DrKonqi::debuggerManager()->backtraceGenerator()->sentryPayload(); 0323 auto document = QJsonDocument::fromJson(eventPayload); 0324 auto hash = document.object().toVariantMap(); 0325 0326 // replace the timestamp with the real timestamp (possibly originating in journald) 0327 hash.insert(u"timestamp"_s, DrKonqi::crashedApplication()->datetime().toUTC().toString(Qt::ISODateWithMs)); 0328 0329 { 0330 QList<QVariant> breadcrumbs; 0331 const auto logs = DrKonqi::crashedApplication()->m_logs; 0332 for (const auto &logEntry : logs) { 0333 QVariantHash breadcrumb; 0334 0335 breadcrumb.insert(u"type"_s, u"debug"_s); 0336 breadcrumb.insert(u"category"_s, logEntry.value("QT_CATEGORY"_ba, "default"_ba)); 0337 breadcrumb.insert(u"message"_s, logEntry.value("MESSAGE"_ba)); 0338 breadcrumb.insert(u"level"_s, journalPriorityToSentryLevel(logEntry.value("PRIORITY"_ba))); 0339 auto ok = false; 0340 auto realtime = logEntry.value("_SOURCE_REALTIME_TIMESTAMP"_ba).toULongLong(&ok); 0341 if (ok && realtime > 0) { 0342 std::chrono::microseconds timestamp(realtime); 0343 std::chrono::duration<double> timestampDouble(timestamp); 0344 breadcrumb.insert(u"timestamp"_s, QJsonValue(timestampDouble.count())); 0345 } 0346 0347 breadcrumbs.push_back(breadcrumb); 0348 } 0349 std::reverse(breadcrumbs.begin(), breadcrumbs.end()); 0350 hash.insert(u"breadcrumbs"_s, breadcrumbs); 0351 } 0352 0353 { 0354 constexpr auto CONTEXTS_KEY = "contexts"_L1; 0355 auto context = hash.take(CONTEXTS_KEY).toHash(); 0356 context.insert(u"gpu"_s, 0357 QVariantHash{ 0358 {u"name"_s, DrKonqi::instance()->m_glRenderer}, // 0359 {u"version"_s, QGuiApplication::platformName()}, 0360 }); 0361 hash.insert(CONTEXTS_KEY, context); 0362 } 0363 0364 if (!DrKonqi::instance()->m_exceptionName.isEmpty()) { 0365 constexpr auto EXCEPTION_KEY = "exception"_L1; 0366 constexpr auto VALUES_KEY = "values"_L1; 0367 auto exception = hash.take(EXCEPTION_KEY).toHash(); 0368 auto values = exception.take(VALUES_KEY).toJsonArray(); 0369 // Preprend since the exception is likely the root of the problem stack. 0370 values.prepend(QJsonObject({ 0371 {u"type"_s, DrKonqi::instance()->m_exceptionName}, 0372 {u"value"_s, DrKonqi::instance()->m_exceptionWhat}, 0373 {u"mechanism"_s, 0374 QJsonObject{ 0375 {u"handled"_s, false}, 0376 {u"synthetic"_s, false}, 0377 {u"type"_s, u"drkonqi"_s}, 0378 }}, 0379 })); 0380 exception.insert(VALUES_KEY, values); 0381 hash.insert(EXCEPTION_KEY, exception); 0382 } 0383 0384 m_sentryPostbox.addEventPayload(QJsonDocument::fromVariant(hash)); 0385 maybePickUpPostbox(); 0386 } 0387 0388 void ReportInterface::prepareCrashEvent() 0389 { 0390 switch (DrKonqi::debuggerManager()->backtraceGenerator()->state()) { 0391 case BacktraceGenerator::Loaded: 0392 return prepareEventPayload(); 0393 case BacktraceGenerator::Loading: 0394 case BacktraceGenerator::NotLoaded: 0395 case BacktraceGenerator::Failed: 0396 case BacktraceGenerator::FailedToStart: 0397 break; 0398 } 0399 static bool connected = false; 0400 if (!connected) { 0401 connected = true; 0402 connect(DrKonqi::debuggerManager()->backtraceGenerator(), &BacktraceGenerator::done, this, [this] { 0403 prepareEventPayload(); 0404 }); 0405 } 0406 if (DrKonqi::debuggerManager()->backtraceGenerator()->state() != BacktraceGenerator::Loading) { 0407 DrKonqi::debuggerManager()->backtraceGenerator()->start(); 0408 } 0409 } 0410 0411 void ReportInterface::prepareCrashComment() 0412 { 0413 m_sentryPostbox.addUserFeedback(m_reportTitle + QLatin1Char('\n') + m_reportDetailText + QLatin1Char('\n') + DrKonqi::kdeBugzillaURL() 0414 + QLatin1String("show_bug.cgi?id=%1").arg(QString::number(m_sentReport))); 0415 maybePickUpPostbox(); 0416 } 0417 0418 void ReportInterface::createCrashMessage(const QString &message) 0419 { 0420 m_sentryPostbox.addUserFeedback(message); 0421 maybePickUpPostbox(); 0422 } 0423 0424 void ReportInterface::sendBugReport() 0425 { 0426 prepareCrashEvent(); 0427 0428 bool attach = false; 0429 Bugzilla::NewBug report = newBugReportTemplate(); 0430 report.description = generateReportFullText(ReportInterface::DrKonqiStamp::Include, ReportInterface::Backtrace::Complete); 0431 0432 // If the report is too long try to reduce it, try to not include the 0433 // backtrace and eventually give up. 0434 // Bugzilla has a hard-limit on the server side, if we cannot strip the 0435 // report down enough the submission will simply not work. 0436 // Exhausting the cap with just user input is nigh impossible, so we'll 0437 // forego handling of the report being too long even without without 0438 // backtrace. 0439 // https://bugs.kde.org/show_bug.cgi?id=248807 0440 if (report.description.size() >= s_maxReportSize) { 0441 report.description = generateReportFullText(ReportInterface::DrKonqiStamp::Include, ReportInterface::Backtrace::Reduced); 0442 attach = true; 0443 } 0444 if (report.description.size() >= s_maxReportSize) { 0445 report.description = generateReportFullText(ReportInterface::DrKonqiStamp::Include, ReportInterface::Backtrace::Exclude); 0446 attach = true; 0447 } 0448 Q_ASSERT(!report.description.isEmpty()); 0449 0450 connect(m_bugzillaManager, &BugzillaManager::sendReportErrorInvalidValues, this, &ReportInterface::sendUsingDefaultProduct); 0451 connect(m_bugzillaManager, &BugzillaManager::reportSent, this, [this, attach](int bugId) { 0452 if (attach) { 0453 m_attachToBugNumber = bugId; 0454 attachBacktrace(QStringLiteral("DrKonqi auto-attaching complete backtrace.")); 0455 } else { 0456 m_sentReport = bugId; 0457 prepareCrashComment(); 0458 m_sentryPostbox.deliver(); 0459 maybeDone(); 0460 } 0461 }); 0462 connect(m_bugzillaManager, &BugzillaManager::sendReportError, this, &ReportInterface::sendReportError); 0463 m_bugzillaManager->sendReport(report); 0464 } 0465 0466 void ReportInterface::sendUsingDefaultProduct() const 0467 { 0468 // Fallback function: if some of the custom values fail, we need to reset all the fields to the default 0469 //(and valid) bugzilla values; and try to resend 0470 Bugzilla::NewBug bug = newBugReportTemplate(); 0471 bug.product = QLatin1String("kde"); 0472 bug.component = QLatin1String("general"); 0473 bug.platform = QLatin1String("unspecified"); 0474 bug.description = generateReportFullText(ReportInterface::DrKonqiStamp::Include, ReportInterface::Backtrace::Complete); 0475 m_bugzillaManager->sendReport(bug); 0476 } 0477 0478 void ReportInterface::attachBacktraceWithReport() 0479 { 0480 attachBacktrace(generateAttachmentComment()); 0481 } 0482 0483 void ReportInterface::attachBacktrace(const QString &comment) 0484 { 0485 // The user was added to the CC list, proceed with the attachment 0486 connect(m_bugzillaManager, &BugzillaManager::attachToReportSent, this, &ReportInterface::attachSent); 0487 connect(m_bugzillaManager, &BugzillaManager::attachToReportError, this, &ReportInterface::sendReportError); 0488 0489 QString reportText = generateReportFullText(ReportInterface::DrKonqiStamp::Include, ReportInterface::Backtrace::Complete); 0490 QString filename = getSuggestedKCrashFilename(DrKonqi::crashedApplication()); 0491 QLatin1String summary("New crash information added by DrKonqi"); 0492 0493 // Attach the report. The comment of the attachment also includes the bug description 0494 m_bugzillaManager->attachTextToReport(reportText, filename, summary, m_attachToBugNumber, comment); 0495 } 0496 0497 void ReportInterface::attachSent(int attachId) 0498 { 0499 Q_UNUSED(attachId); 0500 0501 // The bug was attached, consider it "sent" 0502 m_sentReport = attachId; 0503 prepareCrashComment(); 0504 m_sentryPostbox.deliver(); 0505 maybeDone(); 0506 } 0507 0508 QStringList ReportInterface::relatedBugzillaProducts() const 0509 { 0510 return m_productMapping->relatedBugzillaProducts(); 0511 } 0512 0513 bool ReportInterface::isWorthReporting() const 0514 { 0515 if (DrKonqi::ignoreQuality()) { 0516 return true; 0517 } 0518 0519 // Evaluate if the provided information is useful enough to enable the automatic report 0520 bool needToReport = false; 0521 0522 if (!m_userRememberCrashSituation) { 0523 // This should never happen... but... 0524 return false; 0525 } 0526 0527 int rating = selectedOptionsRating(); 0528 0529 BacktraceParser::Usefulness use = DrKonqi::debuggerManager()->backtraceGenerator()->parser()->backtraceUsefulness(); 0530 0531 switch (use) { 0532 case BacktraceParser::ReallyUseful: { 0533 // Perfect backtrace: require at least one option or a 100%-50% reproducible crash 0534 needToReport = (rating >= 2) || (m_reproducible == ReproducibleEverytime || m_reproducible == ReproducibleSometimes); 0535 break; 0536 } 0537 case BacktraceParser::MayBeUseful: { 0538 // Not perfect backtrace: require at least two options or a 100% reproducible crash 0539 needToReport = (rating >= 3) || (m_reproducible == ReproducibleEverytime); 0540 break; 0541 } 0542 case BacktraceParser::ProbablyUseless: 0543 // Bad backtrace: require at least two options and always reproducible (strict) 0544 needToReport = (rating >= 5) && (m_reproducible == ReproducibleEverytime); 0545 break; 0546 case BacktraceParser::Useless: 0547 case BacktraceParser::InvalidUsefulness: { 0548 needToReport = false; 0549 } 0550 } 0551 0552 return needToReport; 0553 } 0554 0555 void ReportInterface::setAttachToBugNumber(uint bugNumber) 0556 { 0557 // If bugNumber>0, the report is going to be attached to bugNumber 0558 m_attachToBugNumber = bugNumber; 0559 Q_EMIT attachToBugNumberChanged(); 0560 } 0561 0562 uint ReportInterface::attachToBugNumber() const 0563 { 0564 return m_attachToBugNumber; 0565 } 0566 0567 BugzillaManager *ReportInterface::bugzillaManager() const 0568 { 0569 return m_bugzillaManager; 0570 } 0571 0572 ProductMapping *ReportInterface::productMapping() const 0573 { 0574 return m_productMapping; 0575 } 0576 0577 void ReportInterface::maybeDone() 0578 { 0579 if (m_sentReport != 0 && m_sentryPostbox.hasDelivered()) { 0580 Q_EMIT done(); 0581 } 0582 }; 0583 0584 ReportInterface *ReportInterface::self() 0585 { 0586 static ReportInterface interface; 0587 return &interface; 0588 } 0589 0590 bool ReportInterface::hasCrashEventSent() const 0591 { 0592 return !isCrashEventSendingEnabled() || m_sentryPostbox.hasDelivered() || m_skipSentry; 0593 } 0594 0595 bool ReportInterface::isCrashEventSendingEnabled() const 0596 { 0597 qCDebug(DRKONQI_LOG) << "sentry:" << Settings::self()->sentry() // 0598 << "forceSentry:" << m_forceSentry // 0599 << "hasDeletedFiles:" << DrKonqi::crashedApplication()->hasDeletedFiles() // 0600 << "skipSentry:" << m_skipSentry; 0601 const auto enabled = Settings::self()->sentry() || m_forceSentry; 0602 return enabled && !DrKonqi::crashedApplication()->hasDeletedFiles(); 0603 } 0604 0605 void ReportInterface::setSendWhenReady(bool send) 0606 { 0607 m_sendWhenReady = send; 0608 maybePickUpPostbox(); 0609 } 0610 0611 void ReportInterface::maybePickUpPostbox() 0612 { 0613 qWarning() << Q_FUNC_INFO; 0614 if (m_sendWhenReady && !m_sentryPostbox.hasDelivered() && m_sentryPostbox.isReadyToDeliver()) { 0615 m_sentryPostbox.deliver(); 0616 } 0617 } 0618 void ReportInterface::sendSentryReport() 0619 { 0620 m_forceSentry = true; 0621 trySentry(); 0622 // sendWhenReady is set by the quit handling in main.cpp 0623 } 0624 0625 void ReportInterface::trySentry() 0626 { 0627 qCDebug(DRKONQI_LOG) << "trying sentry?" << isCrashEventSendingEnabled() << !m_tryingSentry; 0628 if (isCrashEventSendingEnabled() && !m_tryingSentry) { 0629 m_tryingSentry = true; 0630 metaObject()->invokeMethod(this, [this] { 0631 connect(&m_sentryPostbox, &SentryPostbox::hasDeliveredChanged, this, &ReportInterface::crashEventSent); 0632 // Send crash event ASAP, if applicable. Trace quality doesn't matter for it. 0633 prepareCrashEvent(); 0634 }); 0635 } 0636 } 0637 0638 QString ReportInterface::sentryEventId() const 0639 { 0640 return m_sentryPostbox.eventId(); 0641 } 0642 0643 #include "moc_reportinterface.cpp"