forked from qt-creator/qt-creator
debugger: rework plain gdb shutdown logic
This commit is contained in:
@@ -40,6 +40,26 @@ namespace Internal {
|
||||
|
||||
class GdbEngine;
|
||||
|
||||
enum GdbAdapterState
|
||||
{
|
||||
AdapterNotRunning,
|
||||
AdapterStarting,
|
||||
AdapterStarted,
|
||||
AdapterStartFailed,
|
||||
InferiorPreparing,
|
||||
InferiorPrepared,
|
||||
InferiorPreparationFailed,
|
||||
InferiorStarting,
|
||||
InferiorStarted,
|
||||
InferiorStartFailed,
|
||||
InferiorShuttingDown,
|
||||
InferiorShutDown,
|
||||
InferiorShutdownFailed,
|
||||
AdapterShuttingDown,
|
||||
//AdapterShutDown, // use AdapterNotRunning
|
||||
AdapterShutdownFailed,
|
||||
};
|
||||
|
||||
// AbstractGdbAdapter is inherited by PlainGdbAdapter used for local
|
||||
// debugging and TrkGdbAdapter used for on-device debugging.
|
||||
// In the PlainGdbAdapter case it's just a wrapper around a QProcess running
|
||||
@@ -51,10 +71,9 @@ class AbstractGdbAdapter : public QObject
|
||||
|
||||
public:
|
||||
AbstractGdbAdapter(GdbEngine *engine, QObject *parent = 0)
|
||||
: QObject(parent), m_engine(engine)
|
||||
: QObject(parent), m_engine(engine), m_state(AdapterNotRunning)
|
||||
{}
|
||||
|
||||
virtual QProcess::ProcessState state() const = 0;
|
||||
virtual QString errorString() const = 0;
|
||||
virtual QByteArray readAllStandardError() = 0;
|
||||
virtual QByteArray readAllStandardOutput() = 0;
|
||||
@@ -90,8 +109,13 @@ signals:
|
||||
void readyReadStandardOutput();
|
||||
void readyReadStandardError();
|
||||
|
||||
public:
|
||||
virtual GdbAdapterState state() const { return m_state; }
|
||||
protected:
|
||||
virtual void setState(GdbAdapterState state) { m_state = state; }
|
||||
|
||||
GdbEngine * const m_engine;
|
||||
GdbAdapterState m_state;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -661,10 +661,10 @@ void GdbEngine::readGdbStandardOutput()
|
||||
|
||||
void GdbEngine::interruptInferior()
|
||||
{
|
||||
debugMessage(_("GDBENGINE INTERRUPT INFERIOR: %1").arg(m_gdbAdapter->state()));
|
||||
// debugMessage(_("GDBENGINE INTERRUPT INFERIOR: %1").arg(m_gdbAdapter->state()));
|
||||
qq->notifyInferiorStopRequested();
|
||||
|
||||
if (m_gdbAdapter->state() == QProcess::NotRunning) {
|
||||
if (m_gdbAdapter->state() == AdapterNotRunning) {
|
||||
debugMessage(_("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"));
|
||||
qq->notifyInferiorExited();
|
||||
return;
|
||||
@@ -691,6 +691,13 @@ void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
|
||||
|
||||
void GdbEngine::postCommand(const QString &command, AdapterCallback callback,
|
||||
const char *callbackName, const QVariant &cookie)
|
||||
{
|
||||
postCommand(command, NoFlags, callback, callbackName, cookie);
|
||||
}
|
||||
|
||||
void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
|
||||
AdapterCallback callback,
|
||||
const char *callbackName, const QVariant &cookie)
|
||||
{
|
||||
GdbCommand cmd;
|
||||
cmd.command = command;
|
||||
@@ -722,7 +729,7 @@ void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
|
||||
|
||||
void GdbEngine::postCommandHelper(const GdbCommand &cmd)
|
||||
{
|
||||
if (m_gdbAdapter->state() == QProcess::NotRunning) {
|
||||
if (m_gdbAdapter->state() == AdapterNotRunning) {
|
||||
debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + cmd.command);
|
||||
return;
|
||||
}
|
||||
@@ -753,7 +760,7 @@ void GdbEngine::postCommandHelper(const GdbCommand &cmd)
|
||||
void GdbEngine::flushCommand(const GdbCommand &cmd0)
|
||||
{
|
||||
GdbCommand cmd = cmd0;
|
||||
if (m_gdbAdapter->state() != QProcess::Running) {
|
||||
if (m_gdbAdapter->state() == AdapterNotRunning) {
|
||||
emit gdbInputAvailable(LogInput, cmd.command);
|
||||
debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + cmd.command);
|
||||
return;
|
||||
@@ -858,7 +865,7 @@ void GdbEngine::handleResultRecord(const GdbResultRecord &record)
|
||||
|
||||
void GdbEngine::executeDebuggerCommand(const QString &command)
|
||||
{
|
||||
if (m_gdbAdapter->state() != QProcess::Running) {
|
||||
if (m_gdbAdapter->state() == AdapterNotRunning) {
|
||||
debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
|
||||
return;
|
||||
}
|
||||
@@ -1484,7 +1491,9 @@ QString GdbEngine::fullName(const QStringList &candidates)
|
||||
|
||||
void GdbEngine::shutdown()
|
||||
{
|
||||
exitDebugger();
|
||||
m_outputCollector.shutdown();
|
||||
initializeVariables();
|
||||
m_gdbAdapter->shutdownAdapter();
|
||||
}
|
||||
|
||||
void GdbEngine::detachDebugger()
|
||||
@@ -1496,29 +1505,9 @@ void GdbEngine::detachDebugger()
|
||||
|
||||
void GdbEngine::exitDebugger()
|
||||
{
|
||||
debugMessage(_("GDBENGINE EXITDEBUGGER: %1").arg(m_gdbAdapter->state()));
|
||||
if (m_gdbAdapter->state() == QProcess::Starting) {
|
||||
debugMessage(_("WAITING FOR GDB STARTUP TO SHUTDOWN: %1")
|
||||
.arg(m_gdbAdapter->state()));
|
||||
// FIXME: handle this!
|
||||
//m_gdbAdapter->waitForStarted();
|
||||
}
|
||||
if (m_gdbAdapter->state() == QProcess::Running) {
|
||||
debugMessage(_("WAITING FOR RUNNING GDB TO SHUTDOWN: %1")
|
||||
.arg(m_gdbAdapter->state()));
|
||||
if (status() != DebuggerInferiorStopped
|
||||
&& status() != DebuggerProcessStartingUp) {
|
||||
QTC_ASSERT(status() == DebuggerInferiorRunning,
|
||||
qDebug() << "STATUS ON EXITDEBUGGER:" << status());
|
||||
interruptInferior();
|
||||
}
|
||||
if (startMode() == AttachExternal || startMode() == AttachCrashedExternal)
|
||||
postCommand(_("detach"), CB(handleExitHelper));
|
||||
else
|
||||
postCommand(_("kill"), CB(handleExitHelper));
|
||||
} else {
|
||||
exitDebugger2();
|
||||
}
|
||||
m_outputCollector.shutdown();
|
||||
initializeVariables();
|
||||
m_gdbAdapter->shutdownAdapter();
|
||||
}
|
||||
|
||||
void GdbEngine::handleExitHelper(const GdbResultRecord &, const QVariant &)
|
||||
@@ -4316,6 +4305,7 @@ void GdbEngine::handleInferiorStarted()
|
||||
void GdbEngine::handleInferiorShutDown()
|
||||
{
|
||||
debugMessage(_("INFERIOR SUCCESSFULLY SHUT DOWN"));
|
||||
qq->notifyInferiorExited();
|
||||
}
|
||||
|
||||
void GdbEngine::handleInferiorShutdownFailed(const QString &msg)
|
||||
@@ -4325,6 +4315,18 @@ void GdbEngine::handleInferiorShutdownFailed(const QString &msg)
|
||||
tr("Inferior shutdown failed:\n") + msg);
|
||||
}
|
||||
|
||||
void GdbEngine::handleAdapterShutDown()
|
||||
{
|
||||
debugMessage(_("ADAPTER SUCCESSFULLY SHUT DOWN"));
|
||||
}
|
||||
|
||||
void GdbEngine::handleAdapterShutdownFailed(const QString &msg)
|
||||
{
|
||||
debugMessage(_("ADAPTER SHUTDOWN FAILED"));
|
||||
QMessageBox::critical(mainWindow(), tr("Error"),
|
||||
tr("Inferior shutdown failed:\n") + msg);
|
||||
}
|
||||
|
||||
//
|
||||
// Factory
|
||||
//
|
||||
|
||||
@@ -214,6 +214,11 @@ private:
|
||||
AdapterCallback callback,
|
||||
const char *callbackName,
|
||||
const QVariant &cookie = QVariant());
|
||||
void postCommand(const QString &command,
|
||||
GdbCommandFlags flags,
|
||||
AdapterCallback callback,
|
||||
const char *callbackName,
|
||||
const QVariant &cookie = QVariant());
|
||||
void postCommandHelper(const GdbCommand &cmd);
|
||||
void setTokenBarrier();
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace Internal {
|
||||
PlainGdbAdapter::PlainGdbAdapter(GdbEngine *engine, QObject *parent)
|
||||
: AbstractGdbAdapter(engine, parent)
|
||||
{
|
||||
QTC_ASSERT(state() == AdapterNotRunning, qDebug() << state());
|
||||
connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
|
||||
this, SIGNAL(error(QProcess::ProcessError)));
|
||||
connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()),
|
||||
@@ -64,9 +65,9 @@ PlainGdbAdapter::PlainGdbAdapter(GdbEngine *engine, QObject *parent)
|
||||
connect(&m_gdbProc, SIGNAL(readyReadStandardError()),
|
||||
this, SIGNAL(readyReadStandardError()));
|
||||
connect(&m_gdbProc, SIGNAL(started()),
|
||||
this, SIGNAL(adapterStarted()));
|
||||
this, SLOT(handleGdbStarted()));
|
||||
connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
|
||||
this, SLOT(handleFinished(int, QProcess::ExitStatus)));
|
||||
this, SLOT(handleGdbFinished(int, QProcess::ExitStatus)));
|
||||
|
||||
m_stubProc.setMode(Core::Utils::ConsoleProcess::Debug);
|
||||
#ifdef Q_OS_UNIX
|
||||
@@ -84,6 +85,8 @@ PlainGdbAdapter::PlainGdbAdapter(GdbEngine *engine, QObject *parent)
|
||||
|
||||
void PlainGdbAdapter::startAdapter(const DebuggerStartParametersPtr &sp)
|
||||
{
|
||||
QTC_ASSERT(state() == AdapterNotRunning, qDebug() << state());
|
||||
setState(AdapterStarting);
|
||||
debugMessage(_("TRYING TO START ADAPTER"));
|
||||
m_startParameters = sp;
|
||||
|
||||
@@ -121,10 +124,20 @@ void PlainGdbAdapter::startAdapter(const DebuggerStartParametersPtr &sp)
|
||||
m_gdbProc.start(location, gdbArgs);
|
||||
}
|
||||
|
||||
void PlainGdbAdapter::handleGdbStarted()
|
||||
{
|
||||
QTC_ASSERT(state() == AdapterStarting, qDebug() << state());
|
||||
setState(AdapterStarted);
|
||||
emit adapterStarted();
|
||||
}
|
||||
|
||||
void PlainGdbAdapter::prepareInferior()
|
||||
{
|
||||
QTC_ASSERT(state() == AdapterStarted, qDebug() << state());
|
||||
setState(InferiorPreparing);
|
||||
if (!m_startParameters->processArgs.isEmpty())
|
||||
m_engine->postCommand(_("-exec-arguments ") + m_startParameters->processArgs.join(_(" ")));
|
||||
m_engine->postCommand(_("-exec-arguments ")
|
||||
+ m_startParameters->processArgs.join(_(" ")));
|
||||
QFileInfo fi(m_engine->startParameters().executable);
|
||||
m_engine->postCommand(_("-file-exec-and-symbols \"%1\"").arg(fi.absoluteFilePath()),
|
||||
CB(handleFileExecAndSymbols));
|
||||
@@ -132,18 +145,23 @@ void PlainGdbAdapter::prepareInferior()
|
||||
|
||||
void PlainGdbAdapter::handleFileExecAndSymbols(const GdbResultRecord &response, const QVariant &)
|
||||
{
|
||||
QTC_ASSERT(state() == InferiorPreparing, qDebug() << state());
|
||||
if (response.resultClass == GdbResultDone) {
|
||||
//m_breakHandler->clearBreakMarkers();
|
||||
setState(InferiorPrepared);
|
||||
emit inferiorPrepared();
|
||||
} else if (response.resultClass == GdbResultError) {
|
||||
QString msg = tr("Starting executable failed:\n") +
|
||||
__(response.data.findChild("msg").data());
|
||||
setState(InferiorPreparationFailed);
|
||||
emit inferiorPreparationFailed(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void PlainGdbAdapter::startInferior()
|
||||
{
|
||||
QTC_ASSERT(state() == InferiorPrepared, qDebug() << state());
|
||||
setState(InferiorStarting);
|
||||
m_engine->postCommand(_("-exec-run"), CB(handleExecRun));
|
||||
/*
|
||||
#ifdef Q_OS_MAC
|
||||
@@ -163,6 +181,7 @@ void PlainGdbAdapter::startInferior()
|
||||
|
||||
void PlainGdbAdapter::handleInfoTarget(const GdbResultRecord &response, const QVariant &)
|
||||
{
|
||||
QTC_ASSERT(state() == AdapterNotRunning, qDebug() << state());
|
||||
#if defined(Q_OS_MAC)
|
||||
Q_UNUSED(response)
|
||||
#else
|
||||
@@ -190,13 +209,16 @@ void PlainGdbAdapter::handleInfoTarget(const GdbResultRecord &response, const QV
|
||||
|
||||
void PlainGdbAdapter::handleExecRun(const GdbResultRecord &response, const QVariant &)
|
||||
{
|
||||
QTC_ASSERT(state() == InferiorStarting, qDebug() << state());
|
||||
if (response.resultClass == GdbResultRunning) {
|
||||
setState(InferiorStarted);
|
||||
emit inferiorStarted();
|
||||
} else {
|
||||
QTC_ASSERT(response.resultClass == GdbResultError, /**/);
|
||||
const QByteArray &msg = response.data.findChild("msg").data();
|
||||
//QTC_ASSERT(status() == DebuggerInferiorRunning, /**/);
|
||||
//interruptInferior();
|
||||
setState(InferiorStartFailed);
|
||||
emit inferiorStartFailed(msg);
|
||||
}
|
||||
}
|
||||
@@ -221,26 +243,57 @@ void PlainGdbAdapter::interruptInferior()
|
||||
|
||||
void PlainGdbAdapter::shutdownAdapter()
|
||||
{
|
||||
m_engine->postCommand(_("-gdb-exit"), CB(handleExit));
|
||||
// 20s can easily happen when loading webkit debug information
|
||||
if (!m_gdbProc.waitForFinished(20000)) {
|
||||
debugMessage(_("FORCING TERMINATION: %1")
|
||||
.arg(state()));
|
||||
m_gdbProc.terminate();
|
||||
m_gdbProc.waitForFinished(20000);
|
||||
if (state() == InferiorStarted) {
|
||||
setState(InferiorShuttingDown);
|
||||
m_engine->postCommand(_("kill"), CB(handleKill));
|
||||
return;
|
||||
}
|
||||
|
||||
if (state() != QProcess::NotRunning) {
|
||||
debugMessage(_("PROBLEM STOPPING DEBUGGER: STATE %1")
|
||||
if (state() == InferiorShutDown) {
|
||||
setState(AdapterShuttingDown);
|
||||
m_engine->postCommand(_("-gdb-exit"), CB(handleExit));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
if (state() == InferiorShutdownFailed) {
|
||||
m_gdbProc.terminate();
|
||||
// 20s can easily happen when loading webkit debug information
|
||||
m_gdbProc.waitForFinished(20000);
|
||||
setState(AdapterShuttingDown);
|
||||
debugMessage(_("FORCING TERMINATION: %1")
|
||||
.arg(state()));
|
||||
m_gdbProc.kill();
|
||||
if (state() != QProcess::NotRunning) {
|
||||
debugMessage(_("PROBLEM STOPPING DEBUGGER: STATE %1")
|
||||
.arg(state()));
|
||||
m_gdbProc.kill();
|
||||
}
|
||||
m_engine->postCommand(_("-gdb-exit"), CB(handleExit));
|
||||
return;
|
||||
}
|
||||
|
||||
*/
|
||||
QTC_ASSERT(state() == AdapterNotRunning, qDebug() << state());
|
||||
}
|
||||
|
||||
void PlainGdbAdapter::handleKill(const GdbResultRecord &response, const QVariant &)
|
||||
{
|
||||
if (response.resultClass == GdbResultDone) {
|
||||
setState(InferiorShutDown);
|
||||
emit inferiorShutDown();
|
||||
shutdownAdapter(); // re-iterate...
|
||||
} else if (response.resultClass == GdbResultError) {
|
||||
QString msg = tr("Inferior process could not be stopped:\n") +
|
||||
__(response.data.findChild("msg").data());
|
||||
setState(InferiorShutdownFailed);
|
||||
emit inferiorShutdownFailed(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void PlainGdbAdapter::handleExit(const GdbResultRecord &response, const QVariant &)
|
||||
{
|
||||
if (response.resultClass == GdbResultDone) {
|
||||
emit adapterShutDown();
|
||||
// don't set state here, this will be handled in handleGdbFinished()
|
||||
} else if (response.resultClass == GdbResultError) {
|
||||
QString msg = tr("Gdb process could not be stopped:\n") +
|
||||
__(response.data.findChild("msg").data());
|
||||
@@ -248,9 +301,11 @@ void PlainGdbAdapter::handleExit(const GdbResultRecord &response, const QVariant
|
||||
}
|
||||
}
|
||||
|
||||
void PlainGdbAdapter::handleFinished(int, QProcess::ExitStatus)
|
||||
void PlainGdbAdapter::handleGdbFinished(int, QProcess::ExitStatus)
|
||||
{
|
||||
debugMessage(_("GDB PROESS FINISHED"));
|
||||
debugMessage(_("GDB PROESS FINISHED"));
|
||||
setState(AdapterNotRunning);
|
||||
emit adapterShutDown();
|
||||
}
|
||||
|
||||
void PlainGdbAdapter::shutdownInferior()
|
||||
@@ -287,5 +342,6 @@ void PlainGdbAdapter::emitAdapterStartFailed(const QString &msg)
|
||||
m_stubProc.blockSignals(false);
|
||||
emit adapterStartFailed(msg);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
@@ -57,7 +57,6 @@ public:
|
||||
|
||||
//void kill() { m_gdbProc.kill(); }
|
||||
//void terminate() { m_gdbProc.terminate(); }
|
||||
QProcess::ProcessState state() const { return m_gdbProc.state(); }
|
||||
QString errorString() const { return m_gdbProc.errorString(); }
|
||||
QByteArray readAllStandardError() { return m_gdbProc.readAllStandardError(); }
|
||||
QByteArray readAllStandardOutput() { return m_gdbProc.readAllStandardOutput(); }
|
||||
@@ -75,6 +74,7 @@ public:
|
||||
|
||||
private:
|
||||
void handleFileExecAndSymbols(const GdbResultRecord &, const QVariant &);
|
||||
void handleKill(const GdbResultRecord &, const QVariant &);
|
||||
void handleExit(const GdbResultRecord &, const QVariant &);
|
||||
void handleStubAttached(const GdbResultRecord &, const QVariant &);
|
||||
void handleExecRun(const GdbResultRecord &response, const QVariant &);
|
||||
@@ -82,7 +82,8 @@ private:
|
||||
|
||||
void debugMessage(const QString &msg) { m_engine->debugMessage(msg); }
|
||||
void emitAdapterStartFailed(const QString &msg);
|
||||
Q_SLOT void handleFinished(int, QProcess::ExitStatus);
|
||||
Q_SLOT void handleGdbFinished(int, QProcess::ExitStatus);
|
||||
Q_SLOT void handleGdbStarted();
|
||||
Q_SLOT void stubStarted();
|
||||
Q_SLOT void stubError(const QString &msg);
|
||||
|
||||
|
||||
@@ -1488,9 +1488,10 @@ bool TrkGdbAdapter::waitForFinished(int msecs)
|
||||
return m_gdbProc.waitForFinished(msecs);
|
||||
}
|
||||
|
||||
QProcess::ProcessState TrkGdbAdapter::state() const
|
||||
GdbAdapterState TrkGdbAdapter::state() const
|
||||
{
|
||||
return m_gdbProc.state();
|
||||
//return m_gdbProc.state();
|
||||
return AdapterNotRunning; // FIXME
|
||||
}
|
||||
|
||||
QString TrkGdbAdapter::errorString() const
|
||||
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
void kill();
|
||||
void terminate();
|
||||
bool waitForFinished(int msecs = 30000);
|
||||
QProcess::ProcessState state() const;
|
||||
GdbAdapterState state() const;
|
||||
QString errorString() const;
|
||||
QByteArray readAllStandardError();
|
||||
QByteArray readAllStandardOutput();
|
||||
|
||||
Reference in New Issue
Block a user