Warning, file /plasma/drkonqi/src/backtracewidget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /******************************************************************* 0002 * backtracewidget.cpp 0003 * SPDX-FileCopyrightText: 2009, 2010 Dario Andres Rodriguez <andresbajotierra@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 * 0007 ******************************************************************/ 0008 0009 #include "backtracewidget.h" 0010 0011 #include <QDebug> 0012 #include <QLabel> 0013 #include <QScrollBar> 0014 0015 #include <KLocalizedString> 0016 #include <KMessageBox> 0017 #include <KSyntaxHighlighting/Definition> 0018 #include <KSyntaxHighlighting/Repository> 0019 #include <KSyntaxHighlighting/SyntaxHighlighter> 0020 #include <KSyntaxHighlighting/Theme> 0021 #include <qdesktopservices.h> 0022 0023 #include "backtracegenerator.h" 0024 #include "backtraceratingwidget.h" 0025 #include "crashedapplication.h" 0026 #include "debuggermanager.h" 0027 #include "drkonqi.h" 0028 #include "drkonqi_globals.h" 0029 #include "parser/backtraceparser.h" 0030 0031 static const char extraDetailsLabelMargin[] = " margin: 5px; "; 0032 0033 BacktraceWidget::BacktraceWidget(BacktraceGenerator *generator, QWidget *parent, bool showToggleBacktrace) 0034 : QWidget(parent) 0035 , m_btGenerator(generator) 0036 { 0037 ui.setupUi(this); 0038 0039 // Debug package installer 0040 m_debugPackageInstaller = new DebugPackageInstaller(this); 0041 connect(m_debugPackageInstaller, &DebugPackageInstaller::error, this, &BacktraceWidget::debugPackageError); 0042 connect(m_debugPackageInstaller, &DebugPackageInstaller::packagesInstalled, this, &BacktraceWidget::regenerateBacktrace); 0043 connect(m_debugPackageInstaller, &DebugPackageInstaller::canceled, this, &BacktraceWidget::debugPackageCanceled); 0044 0045 connect(m_btGenerator, &BacktraceGenerator::starting, this, &BacktraceWidget::setAsLoading); 0046 connect(m_btGenerator, &BacktraceGenerator::done, this, &BacktraceWidget::loadData); 0047 connect(m_btGenerator, &BacktraceGenerator::someError, this, &BacktraceWidget::loadData); 0048 connect(m_btGenerator, &BacktraceGenerator::failedToStart, this, &BacktraceWidget::loadData); 0049 connect(m_btGenerator, &BacktraceGenerator::newLine, this, &BacktraceWidget::backtraceNewLine); 0050 0051 connect(ui.m_extraDetailsLabel, &QLabel::linkActivated, this, &BacktraceWidget::extraDetailsLinkActivated); 0052 ui.m_extraDetailsLabel->setVisible(false); 0053 ui.m_extraDetailsLabel->setStyleSheet(QLatin1String(extraDetailsLabelMargin)); 0054 0055 // Setup the buttons 0056 KGuiItem::assign(ui.m_reloadBacktraceButton, 0057 KGuiItem2(i18nc("@action:button", "&Reload"), 0058 QIcon::fromTheme(QStringLiteral("view-refresh")), 0059 i18nc("@info:tooltip", 0060 "Use this button to " 0061 "reload the crash information (backtrace). This is useful when you have " 0062 "installed the proper debug symbol packages and you want to obtain " 0063 "a better backtrace."))); 0064 connect(ui.m_reloadBacktraceButton, &QPushButton::clicked, this, &BacktraceWidget::regenerateBacktrace); 0065 0066 KGuiItem::assign(ui.m_installDebugButton, 0067 KGuiItem2(i18nc("@action:button", "&Install Debug Symbols"), 0068 QIcon::fromTheme(QStringLiteral("system-software-update")), 0069 i18nc("@info:tooltip", 0070 "Use this button to " 0071 "install the missing debug symbols packages."))); 0072 ui.m_installDebugButton->setVisible(false); 0073 connect(ui.m_installDebugButton, &QPushButton::clicked, this, &BacktraceWidget::installDebugPackages); 0074 if (DrKonqi::crashedApplication()->hasDeletedFiles()) { 0075 ui.m_installDebugButton->setEnabled(false); 0076 ui.m_installDebugButton->setToolTip(i18nc("@info:tooltip", 0077 "Symbol installation is unavailable because the application " 0078 "was updated or uninstalled after it had been started.")); 0079 } 0080 0081 KGuiItem::assign(ui.m_copyButton, 0082 KGuiItem2(QString(), 0083 QIcon::fromTheme(QStringLiteral("edit-copy")), 0084 i18nc("@info:tooltip", 0085 "Use this button to copy the " 0086 "crash information (backtrace) to the clipboard."))); 0087 connect(ui.m_copyButton, &QPushButton::clicked, this, &BacktraceWidget::copyClicked); 0088 ui.m_copyButton->setEnabled(false); 0089 0090 KGuiItem::assign(ui.m_saveButton, 0091 KGuiItem2(QString(), 0092 QIcon::fromTheme(QStringLiteral("document-save")), 0093 i18nc("@info:tooltip", 0094 "Use this button to save the " 0095 "crash information (backtrace) to a file. This is useful " 0096 "if you want to take a look at it or to report the bug " 0097 "later."))); 0098 connect(ui.m_saveButton, &QPushButton::clicked, this, &BacktraceWidget::saveClicked); 0099 ui.m_saveButton->setEnabled(false); 0100 0101 // Create the rating widget 0102 m_backtraceRatingWidget = new BacktraceRatingWidget(ui.m_statusWidget); 0103 ui.m_statusWidget->addCustomStatusWidget(m_backtraceRatingWidget); 0104 0105 ui.m_statusWidget->setIdle(QString()); 0106 0107 // Do we need the "Show backtrace" toggle action ? 0108 if (!showToggleBacktrace) { 0109 ui.mainLayout->removeWidget(ui.m_toggleBacktraceCheckBox); 0110 ui.m_toggleBacktraceCheckBox->setVisible(false); 0111 toggleBacktrace(true); 0112 } else { 0113 // Generate help widget 0114 ui.m_backtraceHelpLabel->setText( 0115 i18n("<h2>What is a \"backtrace\" ?</h2><p>A backtrace basically describes what was " 0116 "happening inside the application when it crashed, so the developers may track " 0117 "down where the mess started. They may look meaningless to you, but they might " 0118 "actually contain a wealth of useful information.<br />Backtraces are commonly " 0119 "used during interactive and post-mortem debugging.</p>")); 0120 ui.m_backtraceHelpIcon->setPixmap(QIcon::fromTheme(QStringLiteral("help-hint")).pixmap(48, 48)); 0121 connect(ui.m_toggleBacktraceCheckBox, &QCheckBox::toggled, this, &BacktraceWidget::toggleBacktrace); 0122 toggleBacktrace(false); 0123 } 0124 0125 ui.m_backtraceEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 0126 } 0127 0128 void BacktraceWidget::setAsLoading() 0129 { 0130 // remove the syntax highlighter 0131 delete m_highlighter; 0132 m_highlighter = nullptr; 0133 0134 // Set the widget as loading and disable all the action buttons 0135 ui.m_backtraceEdit->setText(i18nc("@info:status", "Loading...")); 0136 ui.m_backtraceEdit->setEnabled(false); 0137 0138 ui.m_statusWidget->setBusy(i18nc("@info:status", "Generating backtrace... (this may take some time)")); 0139 m_backtraceRatingWidget->setUsefulness(BacktraceParser::Useless); 0140 m_backtraceRatingWidget->setState(BacktraceGenerator::Loading); 0141 0142 ui.m_extraDetailsLabel->setVisible(false); 0143 ui.m_extraDetailsLabel->clear(); 0144 0145 ui.m_installDebugButton->setVisible(false); 0146 ui.m_reloadBacktraceButton->setEnabled(false); 0147 0148 ui.m_copyButton->setEnabled(false); 0149 ui.m_saveButton->setEnabled(false); 0150 } 0151 0152 // Force backtrace generation 0153 void BacktraceWidget::regenerateBacktrace() 0154 { 0155 setAsLoading(); 0156 0157 if (!DrKonqi::debuggerManager()->debuggerIsRunning()) { 0158 m_btGenerator->start(); 0159 } else { 0160 anotherDebuggerRunning(); 0161 } 0162 0163 Q_EMIT stateChanged(); 0164 } 0165 0166 void BacktraceWidget::generateBacktrace() 0167 { 0168 if (m_btGenerator->state() == BacktraceGenerator::NotLoaded) { 0169 // First backtrace generation 0170 regenerateBacktrace(); 0171 } else if (m_btGenerator->state() == BacktraceGenerator::Loading) { 0172 // Set in loading state, the widget will catch the backtrace events anyway 0173 setAsLoading(); 0174 Q_EMIT stateChanged(); 0175 } else { 0176 //*Finished* states 0177 setAsLoading(); 0178 Q_EMIT stateChanged(); 0179 // Load already generated information 0180 loadData(); 0181 } 0182 } 0183 0184 void BacktraceWidget::anotherDebuggerRunning() 0185 { 0186 // As another debugger is running, we should disable the actions and notify the user 0187 ui.m_backtraceEdit->setEnabled(false); 0188 ui.m_backtraceEdit->setText(i18nc("@info", 0189 "Another debugger is currently debugging the " 0190 "same application. The crash information could not be fetched.")); 0191 m_backtraceRatingWidget->setState(BacktraceGenerator::Failed); 0192 m_backtraceRatingWidget->setUsefulness(BacktraceParser::Useless); 0193 ui.m_statusWidget->setIdle(i18nc("@info:status", "The crash information could not be fetched.")); 0194 ui.m_extraDetailsLabel->setVisible(true); 0195 ui.m_extraDetailsLabel->setText(xi18nc("@info/rich", 0196 "Another debugging process is attached to " 0197 "the crashed application. Therefore, the DrKonqi debugger cannot " 0198 "fetch the backtrace. Please close the other debugger and " 0199 "click <interface>Reload</interface>.")); 0200 ui.m_installDebugButton->setVisible(false); 0201 ui.m_reloadBacktraceButton->setEnabled(true); 0202 } 0203 0204 void BacktraceWidget::loadData() 0205 { 0206 // Load the backtrace data from the generator 0207 m_backtraceRatingWidget->setState(m_btGenerator->state()); 0208 0209 if (m_btGenerator->state() == BacktraceGenerator::Loaded) { 0210 ui.m_backtraceEdit->setEnabled(true); 0211 ui.m_backtraceEdit->setPlainText(m_btGenerator->backtrace()); 0212 0213 // scroll to crash 0214 QTextCursor crashCursor = ui.m_backtraceEdit->document()->find(QStringLiteral("[KCrash Handler]")); 0215 if (crashCursor.isNull()) { 0216 crashCursor = ui.m_backtraceEdit->document()->find(QStringLiteral("KCrash::defaultCrashHandler")); 0217 } 0218 if (!crashCursor.isNull()) { 0219 crashCursor.movePosition(QTextCursor::Up, QTextCursor::MoveAnchor); 0220 ui.m_backtraceEdit->verticalScrollBar()->setValue(ui.m_backtraceEdit->cursorRect(crashCursor).top()); 0221 } 0222 0223 // highlight if possible 0224 if (m_btGenerator->debuggerIsGDB()) { 0225 KSyntaxHighlighting::Repository repository; 0226 m_highlighter = new KSyntaxHighlighting::SyntaxHighlighter(ui.m_backtraceEdit->document()); 0227 m_highlighter->setTheme((palette().color(QPalette::Base).lightness() < 128) ? repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme) 0228 : repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme)); 0229 0230 const auto def = repository.definitionForName(QStringLiteral("GDB Backtrace")); 0231 m_highlighter->setDefinition(def); 0232 } 0233 0234 BacktraceParser *btParser = m_btGenerator->parser(); 0235 m_backtraceRatingWidget->setUsefulness(btParser->backtraceUsefulness()); 0236 0237 // Generate the text to put in the status widget (backtrace usefulness) 0238 QString usefulnessText; 0239 switch (btParser->backtraceUsefulness()) { 0240 case BacktraceParser::ReallyUseful: 0241 usefulnessText = i18nc("@info", "The generated crash information is useful"); 0242 break; 0243 case BacktraceParser::MayBeUseful: 0244 usefulnessText = i18nc("@info", "The generated crash information may be useful"); 0245 break; 0246 case BacktraceParser::ProbablyUseless: 0247 usefulnessText = i18nc("@info", "The generated crash information is probably not useful"); 0248 break; 0249 case BacktraceParser::Useless: 0250 usefulnessText = i18nc("@info", "The generated crash information is not useful"); 0251 break; 0252 default: 0253 // let's hope nobody will ever see this... ;) 0254 usefulnessText = i18nc("@info", 0255 "The rating of this crash information is invalid. " 0256 "This is a bug in DrKonqi itself."); 0257 break; 0258 } 0259 ui.m_statusWidget->setIdle(usefulnessText); 0260 0261 if (btParser->backtraceUsefulness() != BacktraceParser::ReallyUseful) { 0262 // Not a perfect bactrace, ask the user to try to improve it 0263 ui.m_extraDetailsLabel->setVisible(true); 0264 if (canInstallDebugPackages()) { 0265 // The script to install the debug packages is available 0266 ui.m_extraDetailsLabel->setText(xi18nc("@info/rich", 0267 "You can click the <interface>" 0268 "Install Debug Symbols</interface> button in order to automatically " 0269 "install the missing debugging information packages. If this method " 0270 "does not work: please read <link url='%1'>How to " 0271 "create useful crash reports</link> to learn how to get a useful " 0272 "backtrace; install the needed packages (<link url='%2'>" 0273 "list of files</link>) and click the " 0274 "<interface>Reload</interface> button.", 0275 QLatin1String(TECHBASE_HOWTO_DOC), 0276 QLatin1String("#missingDebugPackages"))); 0277 ui.m_installDebugButton->setVisible(true); 0278 // Retrieve the libraries with missing debug info 0279 const QStringList missingLibraries = btParser->librariesWithMissingDebugSymbols(); 0280 m_debugPackageInstaller->setMissingLibraries(missingLibraries); 0281 } else { 0282 // No automated method to install the missing debug info 0283 // Tell the user to read the howto and reload 0284 ui.m_extraDetailsLabel->setText(xi18nc("@info/rich", 0285 "Please read <link url='%1'>How to " 0286 "create useful crash reports</link> to learn how to get a useful " 0287 "backtrace; install the needed packages (<link url='%2'>" 0288 "list of files</link>) and click the " 0289 "<interface>Reload</interface> button.", 0290 QLatin1String(TECHBASE_HOWTO_DOC), 0291 QLatin1String("#missingDebugPackages"))); 0292 } 0293 } 0294 0295 ui.m_copyButton->setEnabled(true); 0296 ui.m_saveButton->setEnabled(true); 0297 } else if (m_btGenerator->state() == BacktraceGenerator::Failed) { 0298 // The backtrace could not be generated because the debugger had an error 0299 m_backtraceRatingWidget->setUsefulness(BacktraceParser::Useless); 0300 0301 ui.m_statusWidget->setIdle(i18nc("@info:status", "The debugger has quit unexpectedly.")); 0302 0303 ui.m_backtraceEdit->setPlainText(i18nc("@info:status", "The crash information could not be generated.")); 0304 0305 ui.m_extraDetailsLabel->setVisible(true); 0306 ui.m_extraDetailsLabel->setText(xi18nc("@info/rich", 0307 "You could try to regenerate the " 0308 "backtrace by clicking the <interface>Reload" 0309 "</interface> button.")); 0310 } else if (m_btGenerator->state() == BacktraceGenerator::FailedToStart) { 0311 // The backtrace could not be generated because the debugger could not start (missing) 0312 // Tell the user to install it. 0313 m_backtraceRatingWidget->setUsefulness(BacktraceParser::Useless); 0314 0315 ui.m_statusWidget->setIdle(i18nc("@info:status", 0316 "<strong>The debugger application is missing or " 0317 "could not be launched.</strong>")); 0318 0319 ui.m_backtraceEdit->setPlainText(i18nc("@info:status", "The crash information could not be generated.")); 0320 ui.m_extraDetailsLabel->setVisible(true); 0321 ui.m_extraDetailsLabel->setText(xi18nc("@info/rich", 0322 "<strong>You need to first install the debugger " 0323 "application (%1) then click the <interface>Reload" 0324 "</interface> button.</strong>", 0325 m_btGenerator->debuggerName())); 0326 } 0327 0328 ui.m_reloadBacktraceButton->setEnabled(true); 0329 0330 Q_EMIT stateChanged(); 0331 } 0332 0333 void BacktraceWidget::backtraceNewLine(const QString &line) 0334 { 0335 // While loading the backtrace (unparsed) a new line was sent from the debugger, append it 0336 ui.m_backtraceEdit->append(line.trimmed()); 0337 } 0338 0339 void BacktraceWidget::copyClicked() 0340 { 0341 ui.m_backtraceEdit->selectAll(); 0342 ui.m_backtraceEdit->copy(); 0343 } 0344 0345 void BacktraceWidget::saveClicked() 0346 { 0347 DrKonqi::saveReport(m_btGenerator->backtrace(), this); 0348 } 0349 0350 void BacktraceWidget::highlightExtraDetailsLabel(bool highlight) 0351 { 0352 const QString stylesheet = ((!highlight) ? QLatin1String("border-width: 0px;") 0353 : QLatin1String("border-width: 2px; " 0354 "border-style: solid; " 0355 "border-color: red;")) 0356 + QLatin1String(extraDetailsLabelMargin); 0357 0358 ui.m_extraDetailsLabel->setStyleSheet(stylesheet); 0359 } 0360 0361 void BacktraceWidget::focusImproveBacktraceButton() 0362 { 0363 ui.m_installDebugButton->setFocus(); 0364 } 0365 0366 void BacktraceWidget::installDebugPackages() 0367 { 0368 ui.m_installDebugButton->setVisible(false); 0369 m_debugPackageInstaller->installDebugPackages(); 0370 } 0371 0372 void BacktraceWidget::debugPackageError(const QString &errorMessage) 0373 { 0374 ui.m_installDebugButton->setVisible(true); 0375 KMessageBox::error(this, 0376 errorMessage, 0377 i18nc("@title:window", 0378 "Error during the installation of" 0379 " debug symbols")); 0380 } 0381 0382 void BacktraceWidget::debugPackageCanceled() 0383 { 0384 ui.m_installDebugButton->setVisible(true); 0385 } 0386 0387 bool BacktraceWidget::canInstallDebugPackages() const 0388 { 0389 return m_debugPackageInstaller->canInstallDebugPackages(); 0390 } 0391 0392 void BacktraceWidget::toggleBacktrace(bool show) 0393 { 0394 ui.m_backtraceStack->setCurrentWidget(show ? ui.backtracePage : ui.backtraceHelpPage); 0395 } 0396 0397 void BacktraceWidget::extraDetailsLinkActivated(QString link) 0398 { 0399 if (link.startsWith(QLatin1String("http"))) { 0400 // Open externally 0401 QDesktopServices::openUrl(QUrl(link)); 0402 } else if (link == QLatin1String("#missingDebugPackages")) { 0403 BacktraceParser *btParser = m_btGenerator->parser(); 0404 0405 QStringList missingDbgForFiles = btParser->librariesWithMissingDebugSymbols(); 0406 missingDbgForFiles.insert(0, DrKonqi::crashedApplication()->executable().absoluteFilePath()); 0407 0408 // HTML message 0409 QString message = QStringLiteral("<html>") + i18n("The packages containing debug information for the following application and libraries are missing:") 0410 + QStringLiteral("<br /><ul>"); 0411 0412 for (const QString &string : std::as_const(missingDbgForFiles)) { 0413 message += QLatin1String("<li>") + string + QLatin1String("</li>"); 0414 } 0415 0416 message += QStringLiteral("</ul></html>"); 0417 0418 KMessageBox::information(this, message, i18nc("messagebox title", "Missing debug information packages")); 0419 } 0420 }