Skip to content

Commit

Permalink
Write the gdb backtrace in a file
Browse files Browse the repository at this point in the history
  • Loading branch information
adeas31 committed Feb 15, 2017
1 parent 23b3f86 commit c3ebc92
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 219 deletions.
72 changes: 71 additions & 1 deletion OMEdit/OMEditGUI/CrashReport/CrashReportDialog.cpp
Expand Up @@ -34,6 +34,7 @@
#include "CrashReportDialog.h"
#include "Util/Helper.h"
#include "omc_config.h"
#include "GDBBacktrace.h"

#include <QGridLayout>
#include <QMessageBox>
Expand All @@ -45,13 +46,17 @@
/*!
* \brief CrashReportDialog::CrashReportDialog
*/
CrashReportDialog::CrashReportDialog()
CrashReportDialog::CrashReportDialog(QString stacktrace)
: QDialog(0)
{
// flush all streams before making a crash report so we get full logs.
fflush(NULL);
setWindowTitle(QString(Helper::applicationName).append(" - ").append(Helper::crashReport));
setAttribute(Qt::WA_DeleteOnClose);
// brief stack trace
mStackTrace = stacktrace;
// create the GDB stack trace
createGDBBacktrace();
// set heading
mpCrashReportHeading = Utilities::getHeadingLabel(Helper::crashReport);
// set separator line
Expand Down Expand Up @@ -144,6 +149,71 @@ CrashReportDialog::CrashReportDialog()
setLayout(pMainLayout);
}

/*!
* \brief CrashReportDialog::createGDBBacktrace
* Creates the detailed gdb backtrace. If it fails to create one then uses the simple backtrace.
*/
void CrashReportDialog::createGDBBacktrace()
{
QString errorString;
bool error = false;
QString OMStackTraceFilePath = QString("%1/openmodelica.stacktrace.%2").arg(Utilities::tempDirectory()).arg(Helper::OMCServerName);
// create the GDBBacktrace object
GDBBacktrace *pGDBBacktrace = new GDBBacktrace;
if (pGDBBacktrace->errorOccurred()) {
errorString = pGDBBacktrace->output();
error = true;
} else {
QFile file(OMStackTraceFilePath);
if (file.open(QIODevice::ReadOnly)) {
char buf[1024];
qint64 lineLength = file.readLine(buf, sizeof(buf));
if (lineLength != -1) {
QString lineText(buf);
// if we are unable to attach then set error occurred to true.
if (lineText.startsWith("Could not attach to process", Qt::CaseInsensitive)) {
errorString = lineText + QString(file.readAll());
error = true;
}
}
file.close();
} else {
errorString = GUIMessages::getMessage(GUIMessages::UNABLE_TO_OPEN_FILE).arg(OMStackTraceFilePath);
error = true;
}
}
// if some error has occurred
if (error) {
QMessageBox *pMessageBox = new QMessageBox;
pMessageBox->setWindowTitle(QString("%1 - %2").arg(Helper::applicationName, Helper::error));
pMessageBox->setIcon(QMessageBox::Critical);
pMessageBox->setAttribute(Qt::WA_DeleteOnClose);
pMessageBox->setText(tr("Following error has occurred while retrieving detailed gdb backtrace,\n\n%1").arg(errorString));
pMessageBox->addButton(tr("Try again"), QMessageBox::AcceptRole);
pMessageBox->addButton(tr("Send brief backtrace"), QMessageBox::RejectRole);
int answer = pMessageBox->exec();
switch (answer) {
case QMessageBox::AcceptRole:
createGDBBacktrace();
return;
case QMessageBox::RejectRole:
default:
break;
}
// create a brief backtrace file
QFile stackTraceFile;
stackTraceFile.setFileName(OMStackTraceFilePath);
if (stackTraceFile.open(QIODevice::WriteOnly)) {
QTextStream out(&stackTraceFile);
out.setCodec(Helper::utf8.toStdString().data());
out.setGenerateByteOrderMark(false);
out << mStackTrace;
out.flush();
stackTraceFile.close();
}
}
}

/*!
* \brief CrashReportDialog::sendReport
* Slot activated when mpSendReportButton clicked signal is raised.\n
Expand Down
5 changes: 4 additions & 1 deletion OMEdit/OMEditGUI/CrashReport/CrashReportDialog.h
Expand Up @@ -51,8 +51,9 @@ class CrashReportDialog : public QDialog
{
Q_OBJECT
public:
CrashReportDialog();
CrashReportDialog(QString stacktrace);
private:
QString mStackTrace;
Label *mpCrashReportHeading;
QFrame *mpHorizontalLine;
Label *mpEmailLabel;
Expand All @@ -66,6 +67,8 @@ class CrashReportDialog : public QDialog
QPushButton *mpSendReportButton;
QPushButton *mpCancelButton;
QDialogButtonBox *mpButtonBox;

void createGDBBacktrace();
public slots:
void sendReport();
void reportSent(QNetworkReply *pNetworkReply);
Expand Down
140 changes: 40 additions & 100 deletions OMEdit/OMEditGUI/CrashReport/GDBBacktrace.cpp
Expand Up @@ -39,9 +39,8 @@
#endif
#include "Util/Utilities.h"
#include "Util/Helper.h"
#include "CrashReportDialog.h"

#include <QTemporaryFile>
#include <QProcess>
#include <QFile>

/*!
Expand All @@ -55,122 +54,63 @@
GDBBacktrace::GDBBacktrace(QObject *parent)
: QObject(parent)
{
// GDB process
mpGDBProcess = new QProcess;
connect(mpGDBProcess, SIGNAL(readyRead()), SLOT(readGDBOutput()));
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
connect(mpGDBProcess, SIGNAL(errorOccurred(QProcess::ProcessError)), SLOT(handleGDBProcessError(QProcess::ProcessError)));
#else
connect(mpGDBProcess, SIGNAL(error(QProcess::ProcessError)), SLOT(handleGDBProcessError(QProcess::ProcessError)));
#endif
connect(mpGDBProcess, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(handleGDBProcessFinished(int,QProcess::ExitStatus)));
connect(mpGDBProcess, SIGNAL(finished(int,QProcess::ExitStatus)), mpGDBProcess, SLOT(deleteLater()));
mpGDBProcess->setProcessChannelMode(QProcess::MergedChannels);
QString program = QLatin1String("gdb");
#ifdef WIN32
program = Utilities::getGDBPath();
const qint64 processId = GetCurrentProcessId();
#else
const qint64 processId = getpid();
#endif
mGDBArguments.clear();
mGDBArguments << "-q" << "--nw" << "--nx" << "--batch" << "--command" << createTemporaryCommandsFile() << "--pid" << QString::number(processId);
mOutput.clear();
mErrorOccurred = false;
mpGDBProcess->start(program, mGDBArguments);
mpGDBProcess->waitForFinished(-1);
QStringList gdbArguments;
QString errorString;
QString gdbCommandsFilePath = createCommandsFile(&errorString);
if (gdbCommandsFilePath.isEmpty()) {
mOutput = errorString;
mErrorOccurred = true;
} else {
// GDB process
QProcess gdbProcess;
gdbProcess.setProcessChannelMode(QProcess::MergedChannels);
gdbProcess.setStandardOutputFile(QString("%1/openmodelica.stacktrace.%2").arg(Utilities::tempDirectory()).arg(Helper::OMCServerName));
gdbArguments << "-q" << "--nw" << "--nx" << "--batch" << "--command" << gdbCommandsFilePath << "--pid" << QString::number(processId);
gdbProcess.start(program, gdbArguments);
gdbProcess.waitForFinished(-1);
if (gdbProcess.exitStatus() == QProcess::NormalExit && gdbProcess.exitCode() == 0) {
mOutput = "";
mErrorOccurred = false;
} else {
mOutput = gdbProcess.errorString();
mErrorOccurred = true;
}
}
}

/*!
* \brief GDBBacktrace::createTemporaryCommandsFile
* Creates a temporary file for GDB commands.
* \return
*/
QString GDBBacktrace::createTemporaryCommandsFile()
QString GDBBacktrace::createCommandsFile(QString *errorString)
{
QTemporaryFile *pCommandsFile = new QTemporaryFile;
if (!pCommandsFile->open()) {
mOutput.append("Error: Could not create temporary commands file.");
return QString();
}
connect(this, SIGNAL(finished()), pCommandsFile, SLOT(deleteLater()));
const char GdbBatchCommands[] =
"set height 0\n"
"set width 0\n"
"thread\n"
"thread apply all bt full\n";
if (pCommandsFile->write(GdbBatchCommands) == -1) {
pCommandsFile->close();
mOutput.append("Error: Could not write temporary commands file.");
return QString();
}
pCommandsFile->close();
return pCommandsFile->fileName();
}

/*!
* \brief GDBBacktrace::showCrashReportDialog
* Writes the stack trace file and shows the CrashReportDialog
*/
void GDBBacktrace::showCrashReportDialog()
{
// Dump a stack trace to a file.
QFile stackTraceFile;
stackTraceFile.setFileName(QString("%1/openmodelica.stacktrace.%2").arg(Utilities::tempDirectory()).arg(Helper::OMCServerName));
if (stackTraceFile.open(QIODevice::WriteOnly)) {
QTextStream out(&stackTraceFile);
const char gdbBatchCommands[] = "set height 0\n"
"set width 0\n"
"thread\n"
"thread apply all bt full\n"
"thread apply all bt full\n"
"thread apply all bt full\n";
QFile gdbBacktraceCommandsFile;
QString gdbBacktraceCommandsFilePath = QString("%1omeditbacktracecommands.txt").arg(Utilities::tempDirectory());
gdbBacktraceCommandsFile.setFileName(gdbBacktraceCommandsFilePath);
if (gdbBacktraceCommandsFile.open(QIODevice::WriteOnly)) {
QTextStream out(&gdbBacktraceCommandsFile);
out.setCodec(Helper::utf8.toStdString().data());
out.setGenerateByteOrderMark(false);
out << mOutput;
out << gdbBatchCommands;
out.flush();
stackTraceFile.close();
}
CrashReportDialog *pCrashReportDialog = new CrashReportDialog;
pCrashReportDialog->exec();
emit finished();
}

/*!
* \brief GDBBacktrace::readGDBOutput
* Reads the GDB output.
*/
void GDBBacktrace::readGDBOutput()
{
QString msg = QString(mpGDBProcess->readAll());
// if we are unable to attach then set error occurred to true.
if (msg.startsWith("Could not attach to process", Qt::CaseInsensitive)) {
mErrorOccurred = true;
}
mOutput.append(msg);
}

/*!
* \brief GDBBacktrace::handleGDBProcessError
* Handles the GDB process error.
* \param error
*/
void GDBBacktrace::handleGDBProcessError(QProcess::ProcessError error)
{
Q_UNUSED(error);
mErrorOccurred = true;
mOutput.append(GUIMessages::getMessage(GUIMessages::GDB_ERROR).arg(mpGDBProcess->errorString()).arg(mGDBArguments.join(" ")));
}

/*!
* \brief GDBBacktrace::handleGDBProcessFinished
* Handles the GDB process finished.
* \param exitCode
* \param exitStatus
*/
void GDBBacktrace::handleGDBProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
QString exitCodeStr = tr("GDB process failed. Exited with code %1.").arg(exitCode);
if (exitStatus == QProcess::NormalExit && exitCode == 0) {
if (!mErrorOccurred) {
showCrashReportDialog();
}
gdbBacktraceCommandsFile.close();
return gdbBacktraceCommandsFilePath;
} else {
mErrorOccurred = true;
mOutput.append(mpGDBProcess->errorString() + "\n" + exitCodeStr);
*errorString = QString("Error: Could not create commands file %1.").arg(gdbBacktraceCommandsFilePath);
return "";
}
}
12 changes: 1 addition & 11 deletions OMEdit/OMEditGUI/CrashReport/GDBBacktrace.h
Expand Up @@ -35,7 +35,6 @@
#define GDBBACKTRACE_H

#include <QObject>
#include <QProcess>

class GDBBacktrace : public QObject
{
Expand All @@ -45,19 +44,10 @@ class GDBBacktrace : public QObject
QString output() {return mOutput;}
bool errorOccurred() {return mErrorOccurred;}
private:
QProcess *mpGDBProcess;
QStringList mGDBArguments;
QString mOutput;
bool mErrorOccurred;

QString createTemporaryCommandsFile();
void showCrashReportDialog();
signals:
void finished();
public slots:
void readGDBOutput();
void handleGDBProcessError(QProcess::ProcessError error);
void handleGDBProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
QString createCommandsFile(QString *errorString);
};

#endif // GDBBACKTRACE_H

0 comments on commit c3ebc92

Please sign in to comment.