Commit efb02a30 authored by hjk's avatar hjk
Browse files

debugger: rework plain gdb shutdown logic

parent 1d64e169
......@@ -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()));
if (state() == InferiorStarted) {
setState(InferiorShuttingDown);
m_engine->postCommand(_("kill"), CB(handleKill));
return;
}
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()));
if (state() != QProcess::NotRunning) {
debugMessage(_("PROBLEM STOPPING DEBUGGER: STATE %1")
.arg(state()));
m_gdbProc.kill();
}
m_engine->postCommand(_("-gdb-exit"), CB(handleExit));
return;
}
if (state() != QProcess::NotRunning) {
debugMessage(_("PROBLEM STOPPING DEBUGGER: STATE %1")
.arg(state()));
m_gdbProc.kill();
*/
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();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment