forked from qt-creator/qt-creator
CrashHandler: Add "Attach and Debug" button.
This will launch a new instance of Qt Creator attaching to the crashed instance. The 'Restart' functionality is now represented as a check box since it would be confusing to have one button that restarts the app and quits the crash handler and another one that starts a debugger but keeps the crash handler open (which is necessary, otherwise the crashed app will quit). Change-Id: Id88f418ff73ab7bc72b05753ce2b61bbef8f30cf Reviewed-by: Christian Kandeler <christian.kandeler@digia.com>
This commit is contained in:
@@ -78,6 +78,16 @@ void BacktraceCollector::run(Q_PID pid)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BacktraceCollector::isRunning() const
|
||||||
|
{
|
||||||
|
return d->debugger.state() == QProcess::Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BacktraceCollector::kill()
|
||||||
|
{
|
||||||
|
d->debugger.kill();
|
||||||
|
}
|
||||||
|
|
||||||
void BacktraceCollector::onDebuggerFinished(int exitCode, QProcess::ExitStatus /*exitStatus*/)
|
void BacktraceCollector::onDebuggerFinished(int exitCode, QProcess::ExitStatus /*exitStatus*/)
|
||||||
{
|
{
|
||||||
if (d->errorOccurred) {
|
if (d->errorOccurred) {
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ public:
|
|||||||
~BacktraceCollector();
|
~BacktraceCollector();
|
||||||
|
|
||||||
void run(Q_PID pid);
|
void run(Q_PID pid);
|
||||||
|
bool isRunning() const;
|
||||||
|
void kill();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void error(const QString &errorMessage);
|
void error(const QString &errorMessage);
|
||||||
|
|||||||
@@ -33,13 +33,17 @@
|
|||||||
#include "backtracecollector.h"
|
#include "backtracecollector.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <utils/environment.h>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -48,9 +52,11 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
static const char FileDistroInformation[] = "/etc/lsb-release";
|
static const char FileDistroInformation[] = "/etc/lsb-release";
|
||||||
static const char FileKernelVersion[] = "/proc/version";
|
static const char FileKernelVersion[] = "/proc/version";
|
||||||
|
static const char QtCreatorExecutable[] = "qtcreator";
|
||||||
|
|
||||||
static QString collectLinuxDistributionInfo()
|
static QString collectLinuxDistributionInfo()
|
||||||
{
|
{
|
||||||
@@ -62,33 +68,40 @@ static QString collectKernelVersionInfo()
|
|||||||
return QString::fromLatin1(fileContents(QLatin1String(FileKernelVersion)));
|
return QString::fromLatin1(fileContents(QLatin1String(FileKernelVersion)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convience class for interacting with exec() family of functions.
|
||||||
|
class CExecList : public QVector<char *>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CExecList(const QStringList &list)
|
||||||
|
{
|
||||||
|
foreach (const QString &item, list)
|
||||||
|
append(qstrdup(item.toLatin1().data()));
|
||||||
|
append(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CExecList()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < size(); ++i)
|
||||||
|
delete[] value(i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class CrashHandlerPrivate
|
class CrashHandlerPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CrashHandlerPrivate(pid_t pid, CrashHandler *crashHandler)
|
CrashHandlerPrivate(pid_t pid, CrashHandler *crashHandler)
|
||||||
: pid(pid), dialog(crashHandler), argv(0), envp(0) {}
|
: pid(pid),
|
||||||
|
creatorInPath(Utils::Environment::systemEnvironment().searchInPath(QtCreatorExecutable)),
|
||||||
|
dialog(crashHandler) {}
|
||||||
|
|
||||||
~CrashHandlerPrivate()
|
const pid_t pid;
|
||||||
{
|
const QString creatorInPath; // Backup debugger.
|
||||||
if (argv) {
|
|
||||||
for (int i = 0; argv[i]; ++i)
|
|
||||||
delete[] argv[i];
|
|
||||||
}
|
|
||||||
if (envp) {
|
|
||||||
for (int i = 0; envp[i]; ++i)
|
|
||||||
delete[] envp[i];
|
|
||||||
}
|
|
||||||
free(argv);
|
|
||||||
free(envp);
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t pid;
|
|
||||||
BacktraceCollector backtraceCollector;
|
BacktraceCollector backtraceCollector;
|
||||||
CrashHandlerDialog dialog;
|
CrashHandlerDialog dialog;
|
||||||
|
|
||||||
// For restarting the process.
|
QStringList restartAppCommandLine;
|
||||||
char **argv;
|
QStringList restartAppEnvironment;
|
||||||
char **envp;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CrashHandler::CrashHandler(pid_t pid, QObject *parent)
|
CrashHandler::CrashHandler(pid_t pid, QObject *parent)
|
||||||
@@ -100,10 +113,14 @@ CrashHandler::CrashHandler(pid_t pid, QObject *parent)
|
|||||||
|
|
||||||
d->dialog.appendDebugInfo(collectKernelVersionInfo());
|
d->dialog.appendDebugInfo(collectKernelVersionInfo());
|
||||||
d->dialog.appendDebugInfo(collectLinuxDistributionInfo());
|
d->dialog.appendDebugInfo(collectLinuxDistributionInfo());
|
||||||
d->dialog.show();
|
|
||||||
|
|
||||||
if (!collectRestartAppData()) // If we can't restart the app properly, ...
|
if (!collectRestartAppData()) {
|
||||||
d->dialog.disableRestartAppButton();
|
d->dialog.disableRestartAppCheckBox();
|
||||||
|
if (d->creatorInPath.isEmpty())
|
||||||
|
d->dialog.disableDebugAppButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
d->dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
CrashHandler::~CrashHandler()
|
CrashHandler::~CrashHandler()
|
||||||
@@ -130,8 +147,7 @@ void CrashHandler::onError(const QString &errorMessage)
|
|||||||
void CrashHandler::onBacktraceChunk(const QString &chunk)
|
void CrashHandler::onBacktraceChunk(const QString &chunk)
|
||||||
{
|
{
|
||||||
d->dialog.appendDebugInfo(chunk);
|
d->dialog.appendDebugInfo(chunk);
|
||||||
QTextStream out(stdout);
|
QTextStream(stdout) << chunk;
|
||||||
out << chunk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandler::onBacktraceFinished(const QString &backtrace)
|
void CrashHandler::onBacktraceFinished(const QString &backtrace)
|
||||||
@@ -164,54 +180,40 @@ bool CrashHandler::collectRestartAppData()
|
|||||||
{
|
{
|
||||||
const QString procDir = QString::fromLatin1("/proc/%1").arg(d->pid);
|
const QString procDir = QString::fromLatin1("/proc/%1").arg(d->pid);
|
||||||
|
|
||||||
// Construct d->argv.
|
// Get command line.
|
||||||
// man 5 proc: /proc/[pid]/cmdline
|
// man 5 proc: /proc/[pid]/cmdline
|
||||||
// The command-line arguments appear in this file as a set of strings separated by
|
// The command-line arguments appear in this file as a set of strings separated by
|
||||||
// null bytes ('\0'), with a further null byte after the last string.
|
// null bytes ('\0'), with a further null byte after the last string.
|
||||||
const QString procCmdFileName = procDir + QLatin1String("/cmdline");
|
const QString procCmdFileName = procDir + QLatin1String("/cmdline");
|
||||||
QList<QByteArray> cmdEntries = fileContents(procCmdFileName).split('\0');
|
QList<QByteArray> commandLine = fileContents(procCmdFileName).split('\0');
|
||||||
if (cmdEntries.size() < 2) {
|
if (commandLine.size() < 2) {
|
||||||
qWarning("%s: Unexpected format in file '%s'.\n", Q_FUNC_INFO, qPrintable(procCmdFileName));
|
qWarning("%s: Unexpected format in file '%s'.\n", Q_FUNC_INFO, qPrintable(procCmdFileName));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
cmdEntries.removeLast();
|
commandLine.removeLast();
|
||||||
char * const executable = qstrdup(qPrintable(cmdEntries.takeFirst()));
|
foreach (const QByteArray &item, commandLine)
|
||||||
d->argv = (char **) malloc(sizeof(char*) * (cmdEntries.size() + 2));
|
d->restartAppCommandLine.append(QString::fromLatin1(item));
|
||||||
if (d->argv == 0)
|
|
||||||
qFatal("%s: malloc() failed.\n", Q_FUNC_INFO);
|
|
||||||
d->argv[0] = executable;
|
|
||||||
int i;
|
|
||||||
for (i = 1; i <= cmdEntries.size(); ++i)
|
|
||||||
d->argv[i] = qstrdup(cmdEntries.at(i-1));
|
|
||||||
d->argv[i] = 0;
|
|
||||||
|
|
||||||
// Construct d->envp.
|
// Get environment.
|
||||||
// man 5 proc: /proc/[pid]/environ
|
// man 5 proc: /proc/[pid]/environ
|
||||||
// The entries are separated by null bytes ('\0'), and there may be a null byte at the end.
|
// The entries are separated by null bytes ('\0'), and there may be a null byte at the end.
|
||||||
const QString procEnvFileName = procDir + QLatin1String("/environ");
|
const QString procEnvFileName = procDir + QLatin1String("/environ");
|
||||||
QList<QByteArray> envEntries = fileContents(procEnvFileName).split('\0');
|
QList<QByteArray> environment = fileContents(procEnvFileName).split('\0');
|
||||||
if (envEntries.isEmpty()) {
|
if (environment.isEmpty()) {
|
||||||
qWarning("%s: Unexpected format in file '%s'.\n", Q_FUNC_INFO, qPrintable(procEnvFileName));
|
qWarning("%s: Unexpected format in file '%s'.\n", Q_FUNC_INFO, qPrintable(procEnvFileName));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (envEntries.last().isEmpty())
|
if (environment.last().isEmpty())
|
||||||
envEntries.removeLast();
|
environment.removeLast();
|
||||||
d->envp = (char **) malloc(sizeof(char*) * (envEntries.size() + 1));
|
foreach (const QByteArray &item, environment)
|
||||||
if (d->envp == 0)
|
d->restartAppEnvironment.append(QString::fromLatin1(item));
|
||||||
qFatal("%s: malloc() failed.\n", Q_FUNC_INFO);
|
|
||||||
for (i = 0; i < envEntries.size(); ++i)
|
|
||||||
d->envp[i] = qstrdup(envEntries.at(i));
|
|
||||||
d->envp[i] = 0;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandler::restartApplication()
|
void CrashHandler::runCommand(QStringList commandLine, QStringList environment, WaitMode waitMode)
|
||||||
{
|
{
|
||||||
// TODO: If QTBUG-2284 is resolved, use QProcess::startDetached() here.
|
// TODO: If QTBUG-2284 is resolved, use QProcess::startDetached() here.
|
||||||
// Close the crash handler and start the process again with same environment and
|
|
||||||
// command line arguments.
|
|
||||||
//
|
|
||||||
// We can't use QProcess::startDetached because of bug
|
// We can't use QProcess::startDetached because of bug
|
||||||
//
|
//
|
||||||
// QTBUG-2284
|
// QTBUG-2284
|
||||||
@@ -224,15 +226,19 @@ void CrashHandler::restartApplication()
|
|||||||
case -1: // error
|
case -1: // error
|
||||||
qFatal("%s: fork() failed.", Q_FUNC_INFO);
|
qFatal("%s: fork() failed.", Q_FUNC_INFO);
|
||||||
break;
|
break;
|
||||||
case 0: // child
|
case 0: { // child
|
||||||
qDebug("Restarting Qt Creator with\n");
|
CExecList argv(commandLine);
|
||||||
for (int i = 0; d->argv[i]; ++i)
|
CExecList envp(environment);
|
||||||
qDebug(" %s", d->argv[i]);
|
qDebug("Running\n");
|
||||||
qDebug("\nand environment\n");
|
for (int i = 0; argv[i]; ++i)
|
||||||
for (int i = 0; d->envp[i]; ++i)
|
qDebug(" %s", argv[i]);
|
||||||
qDebug(" %s", d->envp[i]);
|
if (!environment.isEmpty()) {
|
||||||
|
qDebug("\nwith environment:\n");
|
||||||
|
for (int i = 0; envp[i]; ++i)
|
||||||
|
qDebug(" %s", envp[i]);
|
||||||
|
}
|
||||||
|
|
||||||
// The standards pipes must be open, otherwise the restarted Qt Creator will
|
// The standards pipes must be open, otherwise the application will
|
||||||
// receive a SIGPIPE as soon as these are used.
|
// receive a SIGPIPE as soon as these are used.
|
||||||
if (freopen("/dev/null", "r", stdin) == 0)
|
if (freopen("/dev/null", "r", stdin) == 0)
|
||||||
qFatal("%s: freopen() failed for stdin: %s.\n", Q_FUNC_INFO, strerror(errno));
|
qFatal("%s: freopen() failed for stdin: %s.\n", Q_FUNC_INFO, strerror(errno));
|
||||||
@@ -241,10 +247,68 @@ void CrashHandler::restartApplication()
|
|||||||
if (freopen("/dev/null", "w", stderr) == 0)
|
if (freopen("/dev/null", "w", stderr) == 0)
|
||||||
qFatal("%s: freopen() failed for stderr: %s.\n.", Q_FUNC_INFO, strerror(errno));
|
qFatal("%s: freopen() failed for stderr: %s.\n.", Q_FUNC_INFO, strerror(errno));
|
||||||
|
|
||||||
execve(d->argv[0], d->argv, d->envp);
|
if (environment.isEmpty())
|
||||||
|
execv(argv[0], argv.data());
|
||||||
|
else
|
||||||
|
execve(argv[0], argv.data(), envp.data());
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
default: // parent
|
} default: // parent
|
||||||
qApp->quit();
|
if (waitMode == WaitForExit) {
|
||||||
|
while (true) {
|
||||||
|
int status;
|
||||||
|
if (waitpid(pid, &status, 0) == -1) {
|
||||||
|
if (errno == EINTR) // Signal handler of QProcess for SIGCHLD was triggered.
|
||||||
|
continue;
|
||||||
|
perror("waitpid() failed unexpectedly");
|
||||||
|
}
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
qDebug("Child exited with exit code %d.", WEXITSTATUS(status));
|
||||||
|
break;
|
||||||
|
} else if (WIFSIGNALED(status)) {
|
||||||
|
qDebug("Child terminated by signal %d.", WTERMSIG(status));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CrashHandler::restartApplication()
|
||||||
|
{
|
||||||
|
runCommand(d->restartAppCommandLine, d->restartAppEnvironment, DontWaitForExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CrashHandler::debugApplication()
|
||||||
|
{
|
||||||
|
// User requested to debug the app while our debugger is running.
|
||||||
|
if (d->backtraceCollector.isRunning()) {
|
||||||
|
if (!d->dialog.runDebuggerWhileBacktraceNotFinished())
|
||||||
|
return;
|
||||||
|
if (d->backtraceCollector.isRunning()) {
|
||||||
|
d->backtraceCollector.disconnect();
|
||||||
|
d->backtraceCollector.kill();
|
||||||
|
d->dialog.setToFinalState();
|
||||||
|
d->dialog.appendDebugInfo(tr("\n\nCollecting backtrace aborted by user."));
|
||||||
|
QCoreApplication::processEvents(); // Show the last appended output immediately.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare command.
|
||||||
|
QString executable = d->creatorInPath;
|
||||||
|
if (!d->restartAppCommandLine.isEmpty())
|
||||||
|
executable = d->restartAppCommandLine.at(0);
|
||||||
|
const QStringList commandLine = QStringList()
|
||||||
|
<< executable
|
||||||
|
<< QLatin1String("-debug")
|
||||||
|
<< QString::number(d->pid);
|
||||||
|
|
||||||
|
QStringList environment;
|
||||||
|
if (!d->restartAppEnvironment.isEmpty())
|
||||||
|
environment = d->restartAppEnvironment;
|
||||||
|
|
||||||
|
// The UI is blocked/frozen anyway, so hide the dialog while debugging.
|
||||||
|
d->dialog.hide();
|
||||||
|
runCommand(commandLine, environment, WaitForExit);
|
||||||
|
d->dialog.show();
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,10 +55,14 @@ public slots:
|
|||||||
|
|
||||||
void openBugTracker();
|
void openBugTracker();
|
||||||
void restartApplication();
|
void restartApplication();
|
||||||
|
void debugApplication();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool collectRestartAppData();
|
bool collectRestartAppData();
|
||||||
|
|
||||||
|
enum WaitMode { WaitForExit, DontWaitForExit };
|
||||||
|
static void runCommand(QStringList commandLine, QStringList environment, WaitMode waitMode);
|
||||||
|
|
||||||
CrashHandlerPrivate *d;
|
CrashHandlerPrivate *d;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,15 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <app/app_version.h>
|
#include <app/app_version.h>
|
||||||
|
#include <utils/checkablemessagebox.h>
|
||||||
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
static const char SettingsApplication[] = "QtCreator";
|
||||||
|
static const char SettingsKeySkipWarningAbortingBacktrace[]
|
||||||
|
= "CrashHandler/SkipWarningAbortingBacktrace";
|
||||||
|
|
||||||
CrashHandlerDialog::CrashHandlerDialog(CrashHandler *handler, QWidget *parent) :
|
CrashHandlerDialog::CrashHandlerDialog(CrashHandler *handler, QWidget *parent) :
|
||||||
QDialog(parent),
|
QDialog(parent),
|
||||||
@@ -59,8 +65,8 @@ CrashHandlerDialog::CrashHandlerDialog(CrashHandler *handler, QWidget *parent) :
|
|||||||
|
|
||||||
connect(m_ui->copyToClipBoardButton, SIGNAL(clicked()), this, SLOT(copyToClipboardClicked()));
|
connect(m_ui->copyToClipBoardButton, SIGNAL(clicked()), this, SLOT(copyToClipboardClicked()));
|
||||||
connect(m_ui->reportBugButton, SIGNAL(clicked()), m_crashHandler, SLOT(openBugTracker()));
|
connect(m_ui->reportBugButton, SIGNAL(clicked()), m_crashHandler, SLOT(openBugTracker()));
|
||||||
connect(m_ui->restartAppButton, SIGNAL(clicked()), m_crashHandler, SLOT(restartApplication()));
|
connect(m_ui->debugAppButton, SIGNAL(clicked()), m_crashHandler, SLOT(debugApplication()));
|
||||||
connect(m_ui->closeButton, SIGNAL(clicked()), qApp, SLOT(quit()));
|
connect(m_ui->closeButton, SIGNAL(clicked()), this, SLOT(close()));
|
||||||
|
|
||||||
setApplicationInfo();
|
setApplicationInfo();
|
||||||
}
|
}
|
||||||
@@ -70,6 +76,34 @@ CrashHandlerDialog::~CrashHandlerDialog()
|
|||||||
delete m_ui;
|
delete m_ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CrashHandlerDialog::runDebuggerWhileBacktraceNotFinished()
|
||||||
|
{
|
||||||
|
// Check settings.
|
||||||
|
QSettings settings(QSettings::IniFormat, QSettings::UserScope,
|
||||||
|
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
|
||||||
|
QLatin1String(SettingsApplication));
|
||||||
|
if (settings.value(QLatin1String(SettingsKeySkipWarningAbortingBacktrace), false).toBool())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Ask user.
|
||||||
|
const QString title = tr("Run Debugger And Abort Collecting Backtrace?");
|
||||||
|
const QString message = tr(
|
||||||
|
"<html><head/><body>"
|
||||||
|
"<p><b>Run the debugger and abort collecting backtrace?</b></p>"
|
||||||
|
"<p>You have requested to run the debugger while collecting the backtrace was not "
|
||||||
|
"finished.</p>"
|
||||||
|
"</body></html>");
|
||||||
|
const QString checkBoxText = tr("Do not &ask again.");
|
||||||
|
bool checkBoxSetting = false;
|
||||||
|
const QDialogButtonBox::StandardButton button = Utils::CheckableMessageBox::question(this,
|
||||||
|
title, message, checkBoxText, &checkBoxSetting,
|
||||||
|
QDialogButtonBox::Yes|QDialogButtonBox::No, QDialogButtonBox::No);
|
||||||
|
if (checkBoxSetting)
|
||||||
|
settings.setValue(QLatin1String(SettingsKeySkipWarningAbortingBacktrace), checkBoxSetting);
|
||||||
|
|
||||||
|
return button == QDialogButtonBox::Yes;
|
||||||
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::setToFinalState()
|
void CrashHandlerDialog::setToFinalState()
|
||||||
{
|
{
|
||||||
m_ui->progressBar->hide();
|
m_ui->progressBar->hide();
|
||||||
@@ -77,9 +111,14 @@ void CrashHandlerDialog::setToFinalState()
|
|||||||
m_ui->reportBugButton->setEnabled(true);
|
m_ui->reportBugButton->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::disableRestartAppButton()
|
void CrashHandlerDialog::disableRestartAppCheckBox()
|
||||||
{
|
{
|
||||||
m_ui->restartAppButton->setDisabled(true);
|
m_ui->restartAppCheckBox->setDisabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CrashHandlerDialog::disableDebugAppButton()
|
||||||
|
{
|
||||||
|
m_ui->debugAppButton->setDisabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::setApplicationInfo()
|
void CrashHandlerDialog::setApplicationInfo()
|
||||||
@@ -130,3 +169,10 @@ void CrashHandlerDialog::copyToClipboardClicked()
|
|||||||
{
|
{
|
||||||
QApplication::clipboard()->setText(m_ui->debugInfoEdit->toPlainText());
|
QApplication::clipboard()->setText(m_ui->debugInfoEdit->toPlainText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CrashHandlerDialog::close()
|
||||||
|
{
|
||||||
|
if (m_ui->restartAppCheckBox->isEnabled() && m_ui->restartAppCheckBox->isChecked())
|
||||||
|
m_crashHandler->restartApplication();
|
||||||
|
qApp->quit();
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,14 +50,17 @@ public:
|
|||||||
~CrashHandlerDialog();
|
~CrashHandlerDialog();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void disableRestartAppButton();
|
|
||||||
void setApplicationInfo();
|
void setApplicationInfo();
|
||||||
void setToFinalState();
|
|
||||||
void appendDebugInfo(const QString &chunk);
|
void appendDebugInfo(const QString &chunk);
|
||||||
void selectLineWithContents(const QString &text);
|
void selectLineWithContents(const QString &text);
|
||||||
|
void setToFinalState();
|
||||||
|
void disableRestartAppCheckBox();
|
||||||
|
void disableDebugAppButton();
|
||||||
|
bool runDebuggerWhileBacktraceNotFinished();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void copyToClipboardClicked();
|
void copyToClipboardClicked();
|
||||||
|
void close();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CrashHandler *m_crashHandler;
|
CrashHandler *m_crashHandler;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>400</width>
|
<width>500</width>
|
||||||
<height>300</height>
|
<height>300</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@@ -64,6 +64,16 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QTextEdit" name="debugInfoEdit"/>
|
<widget class="QTextEdit" name="debugInfoEdit"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item alignment="Qt::AlignRight">
|
||||||
|
<widget class="QCheckBox" name="restartAppCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Restart Qt Creator on close</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
@@ -71,6 +81,9 @@
|
|||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Copy the whole contents to clipboard.</string>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>C&opy to clipboard</string>
|
<string>C&opy to clipboard</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -81,6 +94,9 @@
|
|||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Open the bug tracker web site.</string>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Report this &bug</string>
|
<string>Report this &bug</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -100,14 +116,23 @@
|
|||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="restartAppButton">
|
<widget class="QPushButton" name="debugAppButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Debug the application with a new instance of Qt Creator. During debugging the crash handler will be hidden.</string>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Restart Qt Creator</string>
|
<string>Attach and &Debug</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="closeButton">
|
<widget class="QPushButton" name="closeButton">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Quit the handler and the crashed application.</string>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Close</string>
|
<string>&Close</string>
|
||||||
</property>
|
</property>
|
||||||
|
|||||||
@@ -11,13 +11,18 @@ SOURCES += \
|
|||||||
backtracecollector.cpp \
|
backtracecollector.cpp \
|
||||||
crashhandlerdialog.cpp \
|
crashhandlerdialog.cpp \
|
||||||
crashhandler.cpp \
|
crashhandler.cpp \
|
||||||
utils.cpp
|
utils.cpp \
|
||||||
|
../../libs/utils/checkablemessagebox.cpp \
|
||||||
|
../../libs/utils/environment.cpp
|
||||||
|
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
backtracecollector.h \
|
backtracecollector.h \
|
||||||
crashhandlerdialog.h \
|
crashhandlerdialog.h \
|
||||||
crashhandler.h \
|
crashhandler.h \
|
||||||
utils.h
|
utils.h \
|
||||||
|
../../libs/utils/checkablemessagebox.h \
|
||||||
|
../../libs/utils/environment.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
crashhandlerdialog.ui
|
crashhandlerdialog.ui
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ QtcTool {
|
|||||||
condition: qbs.targetOS == "linux" && qbs.buildVariant == "debug"
|
condition: qbs.targetOS == "linux" && qbs.buildVariant == "debug"
|
||||||
|
|
||||||
cpp.includePaths: [
|
cpp.includePaths: [
|
||||||
buildDirectory
|
buildDirectory,
|
||||||
|
"../../libs"
|
||||||
]
|
]
|
||||||
|
|
||||||
Depends { name: "cpp" }
|
Depends { name: "cpp" }
|
||||||
@@ -14,6 +15,10 @@ QtcTool {
|
|||||||
Depends { name: "app_version_header" }
|
Depends { name: "app_version_header" }
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
|
"../../libs/utils/checkablemessagebox.cpp",
|
||||||
|
"../../libs/utils/checkablemessagebox.h",
|
||||||
|
"../../libs/utils/environment.cpp",
|
||||||
|
"../../libs/utils/environment.h",
|
||||||
"backtracecollector.cpp",
|
"backtracecollector.cpp",
|
||||||
"backtracecollector.h",
|
"backtracecollector.h",
|
||||||
"crashhandler.cpp",
|
"crashhandler.cpp",
|
||||||
@@ -23,6 +28,6 @@ QtcTool {
|
|||||||
"crashhandlerdialog.ui",
|
"crashhandlerdialog.ui",
|
||||||
"main.cpp",
|
"main.cpp",
|
||||||
"utils.cpp",
|
"utils.cpp",
|
||||||
"utils.h",
|
"utils.h"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user