kde-workspace/drkonqi/backtracewidget.cpp

345 lines
15 KiB
C++
Raw Normal View History

2014-11-15 04:16:00 +02:00
/*******************************************************************
* backtracewidget.cpp
* Copyright 2009,2010 Dario Andres Rodriguez <andresbajotierra@gmail.com>
*
* 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 2 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 <http://www.gnu.org/licenses/>.
*
******************************************************************/
#include "backtracewidget.h"
#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QScrollBar>
#include <KIcon>
#include <KMessageBox>
#include <KLocalizedString>
#include <KToolInvocation>
#include <KGlobalSettings>
#include "drkonqi.h"
#include "backtraceratingwidget.h"
#include "crashedapplication.h"
#include "backtracegenerator.h"
#include "parser/backtraceparser.h"
#include "drkonqi_globals.h"
#include "debuggermanager.h"
#include "gdbhighlighter.h"
static const char extraDetailsLabelMargin[] = " margin: 5px; ";
BacktraceWidget::BacktraceWidget(BacktraceGenerator *generator, QWidget *parent,
bool showToggleBacktrace) :
QWidget(parent),
m_btGenerator(generator),
m_highlighter(0)
{
ui.setupUi(this);
connect(m_btGenerator, SIGNAL(done()) , this, SLOT(loadData()));
connect(m_btGenerator, SIGNAL(someError()) , this, SLOT(loadData()));
connect(m_btGenerator, SIGNAL(failedToStart()) , this, SLOT(loadData()));
connect(m_btGenerator, SIGNAL(newLine(QString)) , this, SLOT(backtraceNewLine(QString)));
connect(ui.m_extraDetailsLabel, SIGNAL(linkActivated(QString)), this,
SLOT(extraDetailsLinkActivated(QString)));
ui.m_extraDetailsLabel->setVisible(false);
ui.m_extraDetailsLabel->setStyleSheet(QLatin1String(extraDetailsLabelMargin));
//Setup the buttons
ui.m_reloadBacktraceButton->setGuiItem(
KGuiItem2(i18nc("@action:button", "&Reload"),
KIcon("view-refresh"), i18nc("@info:tooltip", "Use this button to "
"reload the crash information (backtrace). This is useful when you have "
"installed the proper debug symbol packages and you want to obtain "
"a better backtrace.")));
connect(ui.m_reloadBacktraceButton, SIGNAL(clicked()), this, SLOT(regenerateBacktrace()));
ui.m_copyButton->setGuiItem(KGuiItem2(QString(), KIcon("edit-copy"),
i18nc("@info:tooltip", "Use this button to copy the "
"crash information (backtrace) to the clipboard.")));
connect(ui.m_copyButton, SIGNAL(clicked()) , this, SLOT(copyClicked()));
ui.m_copyButton->setEnabled(false);
ui.m_saveButton->setGuiItem(KGuiItem2(QString(),
KIcon("document-save"),
i18nc("@info:tooltip", "Use this button to save the "
"crash information (backtrace) to a file. This is useful "
"if you want to take a look at it or to report the bug "
"later.")));
connect(ui.m_saveButton, SIGNAL(clicked()) , this, SLOT(saveClicked()));
ui.m_saveButton->setEnabled(false);
//Create the rating widget
m_backtraceRatingWidget = new BacktraceRatingWidget(ui.m_statusWidget);
ui.m_statusWidget->addCustomStatusWidget(m_backtraceRatingWidget);
ui.m_statusWidget->setIdle(QString());
//Do we need the "Show backtrace" toggle action ?
if (!showToggleBacktrace) {
ui.mainLayout->removeWidget(ui.m_toggleBacktraceCheckBox);
ui.m_toggleBacktraceCheckBox->setVisible(false);
toggleBacktrace(true);
} else {
//Generate help widget
ui.m_backtraceHelpLabel->setText(
i18n("<h2>What is a \"backtrace\" ?</h2><p>A backtrace basically describes what was "
"happening inside the application when it crashed, so the developers may track "
"down where the mess started. They may look meaningless to you, but they might "
"actually contain a wealth of useful information.<br />Backtraces are commonly "
"used during interactive and post-mortem debugging.</p>"));
ui.m_backtraceHelpIcon->setPixmap(KIcon("help-hint").pixmap(48,48));
connect(ui.m_toggleBacktraceCheckBox, SIGNAL(toggled(bool)), this,
SLOT(toggleBacktrace(bool)));
toggleBacktrace(false);
}
ui.m_backtraceEdit->setFont( KGlobalSettings::fixedFont() );
}
void BacktraceWidget::setAsLoading()
{
//remove the syntax highlighter
delete m_highlighter;
m_highlighter = 0;
//Set the widget as loading and disable all the action buttons
ui.m_backtraceEdit->setText(i18nc("@info:status", "Loading..."));
ui.m_backtraceEdit->setEnabled(false);
ui.m_statusWidget->setBusy(i18nc("@info:status",
"Generating backtrace... (this may take some time)"));
m_backtraceRatingWidget->setUsefulness(BacktraceParser::Useless);
m_backtraceRatingWidget->setState(BacktraceGenerator::Loading);
ui.m_extraDetailsLabel->setVisible(false);
ui.m_extraDetailsLabel->clear();
ui.m_reloadBacktraceButton->setEnabled(false);
ui.m_copyButton->setEnabled(false);
ui.m_saveButton->setEnabled(false);
}
//Force backtrace generation
void BacktraceWidget::regenerateBacktrace()
{
setAsLoading();
if (!DrKonqi::debuggerManager()->debuggerIsRunning()) {
m_btGenerator->start();
} else {
anotherDebuggerRunning();
}
emit stateChanged();
}
void BacktraceWidget::generateBacktrace()
{
if (m_btGenerator->state() == BacktraceGenerator::NotLoaded) {
//First backtrace generation
regenerateBacktrace();
} else if (m_btGenerator->state() == BacktraceGenerator::Loading) {
//Set in loading state, the widget will catch the backtrace events anyway
setAsLoading();
emit stateChanged();
} else {
//*Finished* states
setAsLoading();
emit stateChanged();
//Load already generated information
loadData();
}
}
void BacktraceWidget::anotherDebuggerRunning()
{
//As another debugger is running, we should disable the actions and notify the user
ui.m_backtraceEdit->setEnabled(false);
ui.m_backtraceEdit->setText(i18nc("@info", "Another debugger is currently debugging the "
"same application. The crash information could not be fetched."));
m_backtraceRatingWidget->setState(BacktraceGenerator::Failed);
m_backtraceRatingWidget->setUsefulness(BacktraceParser::Useless);
ui.m_statusWidget->setIdle(i18nc("@info:status", "The crash information could not be fetched."));
ui.m_extraDetailsLabel->setVisible(true);
ui.m_extraDetailsLabel->setText(i18nc("@info/rich", "Another debugging process is attached to "
"the crashed application. Therefore, the DrKonqi debugger cannot "
"fetch the backtrace. Please close the other debugger and "
"click <interface>Reload</interface>."));
ui.m_reloadBacktraceButton->setEnabled(true);
}
void BacktraceWidget::loadData()
{
//Load the backtrace data from the generator
m_backtraceRatingWidget->setState(m_btGenerator->state());
if (m_btGenerator->state() == BacktraceGenerator::Loaded) {
ui.m_backtraceEdit->setEnabled(true);
ui.m_backtraceEdit->setPlainText(m_btGenerator->backtrace());
// scroll to crash
QTextCursor crashCursor = ui.m_backtraceEdit->document()->find("[KCrash Handler]");
if (!crashCursor.isNull()) {
crashCursor.movePosition(QTextCursor::Up, QTextCursor::MoveAnchor);
ui.m_backtraceEdit->verticalScrollBar()->setValue(ui.m_backtraceEdit->cursorRect(crashCursor).top());
}
// highlight if possible
if (m_btGenerator->debugger().codeName() == "gdb") {
m_highlighter = new GdbHighlighter(ui.m_backtraceEdit->document(),
m_btGenerator->parser()->parsedBacktraceLines());
}
BacktraceParser * btParser = m_btGenerator->parser();
m_backtraceRatingWidget->setUsefulness(btParser->backtraceUsefulness());
//Generate the text to put in the status widget (backtrace usefulness)
QString usefulnessText;
switch (btParser->backtraceUsefulness()) {
case BacktraceParser::ReallyUseful:
usefulnessText = i18nc("@info", "The generated crash information is useful");
break;
case BacktraceParser::MayBeUseful:
usefulnessText = i18nc("@info", "The generated crash information may be useful");
break;
case BacktraceParser::ProbablyUseless:
usefulnessText = i18nc("@info", "The generated crash information is probably not useful");
break;
case BacktraceParser::Useless:
usefulnessText = i18nc("@info", "The generated crash information is not useful");
break;
default:
//let's hope nobody will ever see this... ;)
usefulnessText = i18nc("@info", "The rating of this crash information is invalid. "
"This is a bug in DrKonqi itself.");
break;
}
ui.m_statusWidget->setIdle(usefulnessText);
if (btParser->backtraceUsefulness() != BacktraceParser::ReallyUseful) {
//Not a perfect bactrace, ask the user to try to improve it
ui.m_extraDetailsLabel->setText(i18nc("@info/rich", "Please read <link url='%1'>How to "
"create useful crash reports</link> to learn how to get a useful "
"backtrace; install the needed packages (<link url='%2'>"
"list of files</link>) and click the "
"<interface>Reload</interface> button.",
QLatin1String(TECHBASE_HOWTO_DOC),
QLatin1String("#missingDebugPackages")));
2014-11-15 04:16:00 +02:00
}
ui.m_copyButton->setEnabled(true);
ui.m_saveButton->setEnabled(true);
} else if (m_btGenerator->state() == BacktraceGenerator::Failed) {
//The backtrace could not be generated because the debugger had an error
m_backtraceRatingWidget->setUsefulness(BacktraceParser::Useless);
ui.m_statusWidget->setIdle(i18nc("@info:status", "The debugger has quit unexpectedly."));
ui.m_backtraceEdit->setPlainText(i18nc("@info:status",
"The crash information could not be generated."));
ui.m_extraDetailsLabel->setVisible(true);
ui.m_extraDetailsLabel->setText(i18nc("@info/rich", "You could try to regenerate the "
"backtrace by clicking the <interface>Reload"
"</interface> button."));
} else if (m_btGenerator->state() == BacktraceGenerator::FailedToStart) {
//The backtrace could not be generated because the debugger could not start (missing)
//Tell the user to install it.
m_backtraceRatingWidget->setUsefulness(BacktraceParser::Useless);
ui.m_statusWidget->setIdle(i18nc("@info:status", "<strong>The debugger application is missing or "
"could not be launched.</strong>"));
ui.m_backtraceEdit->setPlainText(i18nc("@info:status",
"The crash information could not be generated."));
ui.m_extraDetailsLabel->setVisible(true);
ui.m_extraDetailsLabel->setText(i18nc("@info/rich", "<strong>You need to first install the debugger "
"application (%1) then click the <interface>Reload"
"</interface> button.</strong>",
m_btGenerator->debugger().name()));
}
ui.m_reloadBacktraceButton->setEnabled(true);
emit stateChanged();
}
void BacktraceWidget::backtraceNewLine(const QString & line)
{
//While loading the backtrace (unparsed) a new line was sent from the debugger, append it
ui.m_backtraceEdit->append(line.trimmed());
}
void BacktraceWidget::copyClicked()
{
ui.m_backtraceEdit->selectAll();
ui.m_backtraceEdit->copy();
}
void BacktraceWidget::saveClicked()
{
DrKonqi::saveReport(m_btGenerator->backtrace(), this);
}
void BacktraceWidget::hilightExtraDetailsLabel(bool hilight)
{
QString stylesheet;
if (hilight) {
stylesheet = QLatin1String("border-width: 2px; "
"border-style: solid; "
"border-color: red;");
} else {
stylesheet = QLatin1String("border-width: 0px;");
}
stylesheet += QLatin1String(extraDetailsLabelMargin);
ui.m_extraDetailsLabel->setStyleSheet(stylesheet);
}
void BacktraceWidget::toggleBacktrace(bool show)
{
ui.m_backtraceStack->setCurrentWidget(show ? ui.backtracePage : ui.backtraceHelpPage);
}
void BacktraceWidget::extraDetailsLinkActivated(QString link)
{
if (link.startsWith(QLatin1String("http"))) {
//Open externally
KToolInvocation::invokeBrowser(link);
} else if (link == QLatin1String("#missingDebugPackages")) {
BacktraceParser * btParser = m_btGenerator->parser();
QStringList missingDbgForFiles = btParser->librariesWithMissingDebugSymbols().toList();
missingDbgForFiles.insert(0, DrKonqi::crashedApplication()->executable().absoluteFilePath());
//HTML message
QString message;
message = "<html>";
message += i18n("The packages containing debug information for the following application and libraries are missing:");
message += "<br /><ul>";
Q_FOREACH(const QString & string, missingDbgForFiles) {
message += "<li>" + string + "</li>";
}
message += "</ul></html>";
KMessageBox::information(this, message, i18nc("messagebox title","Missing debug information packages"));
}
}