Commit b3addf14 authored by Oswald Buddenhagen's avatar Oswald Buddenhagen

fix shutdown paths

this includes:
- move the gdb ownership back to the engine (thus strip down the
  adaptors as far as possible)
- make gdb startup synchronous
- make adapter shutdown synchronous
- fix the state transitions relating to shutdown
parent 4b0060c5
......@@ -824,6 +824,9 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6
void CdbDebugEngine::processTerminated(unsigned long exitCode)
{
manager()->showDebuggerOutput(LogMisc, tr("The process exited with exit code %1.").arg(exitCode));
if (m_engine->state() != InferiorStopping)
setState(InferiorStopping, Q_FUNC_INFO, __LINE__);
setState(InferiorStopped, Q_FUNC_INFO, __LINE__);
setState(InferiorShuttingDown, Q_FUNC_INFO, __LINE__);
m_d->setDebuggeeHandles(0, 0);
m_d->clearForRun();
......@@ -919,14 +922,11 @@ void CdbDebugEnginePrivate::endDebugging(EndDebuggingMode em)
errorMessage.clear();
}
// Clean up resources (open files, etc.)
m_engine->setState(AdapterShuttingDown, Q_FUNC_INFO, __LINE__);
m_engine->setState(EngineShuttingDown, Q_FUNC_INFO, __LINE__);
clearForRun();
const HRESULT hr = m_cif.debugClient->EndSession(DEBUG_END_PASSIVE);
if (SUCCEEDED(hr)) {
m_engine->setState(DebuggerNotReady, Q_FUNC_INFO, __LINE__);
} else {
m_engine->setState(AdapterShutdownFailed, Q_FUNC_INFO, __LINE__);
m_engine->setState(DebuggerNotReady, Q_FUNC_INFO, __LINE__);
m_engine->setState(DebuggerNotReady, Q_FUNC_INFO, __LINE__);
if (!SUCCEEDED(hr)) {
errorMessage = QString::fromLatin1("There were errors trying to end debugging: %1").arg(msgComFailed("EndSession", hr));
manager()->showDebuggerOutput(LogError, errorMessage);
}
......
......@@ -79,15 +79,13 @@ enum DebuggerState
InferiorStopping, // Debuggee running, stop requested
InferiorStopped, // Debuggee stopped
InferiorStopFailed, // Debuggee stopped
InferiorStopFailed, // Debuggee not stopped, will kill debugger
InferiorShuttingDown,
InferiorShutDown,
InferiorShutdownFailed,
AdapterShuttingDown,
//AdapterShutDown, // Use DebuggerNotReady instead
AdapterShutdownFailed,
EngineShuttingDown
};
enum DebuggerStartMode
......
......@@ -101,43 +101,35 @@
// gdbserver, the trk client etc are referred to as 'Adapter',
// whereas the debugged process is referred to as 'Inferior'.
//
// 0 == DebuggerNotReady
// 0 == DebuggerNotReady
// |
// EngineStarting
// EngineStarting
// |
// AdapterStarting --> AdapterStartFailed --> 0
// AdapterStarting --> AdapterStartFailed --> 0
// |
// AdapterStarted
// |
// InferiorStarting --> InferiorStartFailed --> 0
// |
// (core) | (attach) (remote)
// .-----------------<-|->--------------------.
// | v |
// InferiorUnrunnable | |
// | | v
// | | (plain)
// | | (trk)
// | |
// | | .------------------------------------.
// | | v |
// | InferiorRunningRequested v |
// | | | |
// | .---- InferiorRunning | |
// | | | | |
// | | InferiorStopping | |
// | | | | |
// | v v | |
// | |<--- InferiorStopped <-----------' |
// | | | |
// | | `---------------------------------------'
// | |
// | '---> InferiorShuttingDown -> InferiorShutdownFailed
// | |
// | InferiorShutDown
// | |
// | v
// '------------> AdapterShuttingDown -> AdapterShutdownFailed --> 0
// AdapterStarted ------------------------------------.
// | v
// InferiorStarting ----> InferiorStartFailed -------->|
// | |
// (core) | (attach) (term) (remote) |
// .-----------------<-|->------------------. |
// | v | |
// InferiorUnrunnable | (plain) | |
// | | (trk) | |
// | | | |
// | .--> InferiorRunningRequested | |
// | | | | |
// | | InferiorRunning | |
// | | | | |
// | | InferiorStopping | |
// | | | | |
// | '------ InferiorStopped <-----------' |
// | | v
// | InferiorShuttingDown -> InferiorShutdownFailed ---->|
// | | |
// | InferiorShutDown |
// | | |
// '--------> EngineShuttingDown <--------------------------------'
// |
// 0
//
......@@ -208,8 +200,7 @@ static const char *stateName(int s)
SN(InferiorShuttingDown)
SN(InferiorShutDown)
SN(InferiorShutdownFailed)
SN(AdapterShuttingDown)
SN(AdapterShutdownFailed)
SN(EngineShuttingDown)
}
return "<unknown>";
#undef SN
......@@ -1574,7 +1565,7 @@ static bool isAllowedTransition(int from, int to)
case AdapterStarting:
return to == AdapterStarted || to == AdapterStartFailed;
case AdapterStarted:
return to == InferiorStarting;
return to == InferiorStarting || to == EngineShuttingDown;
case AdapterStartFailed:
return to == DebuggerNotReady;
......@@ -1582,37 +1573,38 @@ static bool isAllowedTransition(int from, int to)
return to == InferiorRunningRequested || to == InferiorStopped
|| to == InferiorStartFailed || to == InferiorUnrunnable;
case InferiorStartFailed:
return to == DebuggerNotReady;
return to == EngineShuttingDown;
case InferiorRunningRequested:
return to == InferiorRunning;
case InferiorRunning:
return to == InferiorStopping || to == InferiorShuttingDown;
return to == InferiorStopping;
case InferiorStopping:
return to == InferiorStopped || to == InferiorStopFailed;
case InferiorStopped:
return to == InferiorRunningRequested || to == InferiorShuttingDown;
case InferiorStopFailed:
return to == DebuggerNotReady;
return to == EngineShuttingDown;
case InferiorUnrunnable:
return to == AdapterShuttingDown;
return to == EngineShuttingDown;
case InferiorShuttingDown:
return to == InferiorShutDown || to == InferiorShutdownFailed;
case InferiorShutDown:
return to == AdapterShuttingDown;
return to == EngineShuttingDown;
case InferiorShutdownFailed:
return to == EngineShuttingDown;
case AdapterShuttingDown:
case EngineShuttingDown:
return to == DebuggerNotReady;
default:
qDebug() << "UNKNOWN STATE: " << from;
}
qDebug() << "UNKNOWN STATE:" << from;
return false;
}
void DebuggerManager::setState(DebuggerState state)
void DebuggerManager::setState(DebuggerState state, bool forced)
{
//STATE_DEBUG("STATUS CHANGE: FROM " << stateName(d->m_state)
// << " TO " << stateName(state));
......@@ -1621,7 +1613,7 @@ void DebuggerManager::setState(DebuggerState state)
.arg(stateName(d->m_state)).arg(d->m_state).arg(stateName(state)).arg(state);
//if (!((d->m_state == -1 && state == 0) || (d->m_state == 0 && state == 0)))
// qDebug() << msg;
if (!isAllowedTransition(d->m_state, state))
if (!forced && !isAllowedTransition(d->m_state, state))
qDebug() << "UNEXPECTED STATE TRANSITION: " << msg;
showDebuggerOutput(LogDebug, msg);
......@@ -1715,8 +1707,7 @@ bool DebuggerManager::debuggerActionsEnabled() const
case InferiorShuttingDown:
case InferiorShutDown:
case InferiorShutdownFailed:
case AdapterShuttingDown:
case AdapterShutdownFailed:
case EngineShuttingDown:
break;
}
return false;
......@@ -1793,9 +1784,9 @@ DebuggerState IDebuggerEngine::state() const
return m_manager->state();
}
void IDebuggerEngine::setState(DebuggerState state)
void IDebuggerEngine::setState(DebuggerState state, bool forced)
{
m_manager->setState(state);
m_manager->setState(state, forced);
}
//////////////////////////////////////////////////////////////////////
......
......@@ -285,7 +285,7 @@ private:
void cleanupViews();
void setState(DebuggerState state);
void setState(DebuggerState state, bool forced = false);
//
// internal implementation
......
......@@ -47,35 +47,18 @@ AbstractGdbAdapter::~AbstractGdbAdapter()
disconnect();
}
// This cannot be in the c'tor, as it would not connect the "virtual" slots
void AbstractGdbAdapter::commonInit()
void AbstractGdbAdapter::shutdown()
{
QTC_ASSERT(state() == EngineStarting, qDebug() << state());
connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(handleGdbError(QProcess::ProcessError)));
connect(&m_gdbProc, SIGNAL(started()),
this, SLOT(handleGdbStarted()));
connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(handleGdbFinished(int, QProcess::ExitStatus)));
connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()),
this, SIGNAL(readyReadStandardOutput()));
connect(&m_gdbProc, SIGNAL(readyReadStandardError()),
this, SIGNAL(readyReadStandardError()));
}
QByteArray AbstractGdbAdapter::readAllStandardOutput()
const char *AbstractGdbAdapter::inferiorShutdownCommand() const
{
return m_gdbProc.readAllStandardOutput();
}
QByteArray AbstractGdbAdapter::readAllStandardError()
{
return m_gdbProc.readAllStandardError();
return "kill";
}
void AbstractGdbAdapter::write(const QByteArray &data)
{
m_gdbProc.write(data);
m_engine->m_gdbProc.write(data);
}
bool AbstractGdbAdapter::isTrkAdapter() const
......
......@@ -42,7 +42,7 @@ namespace Internal {
// debugging and TrkGdbAdapter used for on-device debugging.
// In the PlainGdbAdapter case it's just a wrapper around a QProcess running
// gdb, in the TrkGdbAdapter case it's the interface to the gdb process in
// the whole rfomm/gdb/gdbserver combo.
// the whole rfcomm/gdb/gdbserver combo.
class AbstractGdbAdapter : public QObject
{
Q_OBJECT
......@@ -51,34 +51,39 @@ public:
AbstractGdbAdapter(GdbEngine *engine, QObject *parent = 0);
virtual ~AbstractGdbAdapter();
QByteArray readAllStandardOutput();
QByteArray readAllStandardError();
virtual void write(const QByteArray &data);
virtual bool isTrkAdapter() const; // isUtterlyBrokenAdapter
virtual void startAdapter() = 0;
virtual void startInferior() = 0;
virtual void interruptInferior() = 0;
virtual void shutdown() = 0;
virtual void shutdown();
virtual const char *inferiorShutdownCommand() const;
virtual bool dumpersAvailable() const = 0;
static QString msgGdbStopFailed(const QString &why);
static QString msgInferiorStopFailed(const QString &why);
static QString msgAttachedToStoppedInferior();
static QString msgInferiorStarted();
static QString msgInferiorRunning();
static QString msgConnectRemoteServerFailed(const QString &why);
signals:
void adapterStarted();
// Something went wrong with the adapter *before* adapterStarted() was emitted.
// Make sure to clean up everything before emitting this signal.
void adapterStartFailed(const QString &msg, const QString &settingsIdHint);
void adapterShutDown();
void adapterShutdownFailed(const QString &msg);
// Something went wrong with the adapter *after* adapterStarted() was emitted.
// Make sure to clean up everything before emitting this signal.
void adapterCrashed(const QString &msg);
// The adapter is still running just fine, but it failed to acquire a debuggee.
void inferiorStartFailed(const QString &msg);
void inferiorShutDown();
void inferiorShutdownFailed(const QString &msg);
void readyReadStandardOutput();
void readyReadStandardError();
protected:
void commonInit();
DebuggerState state() const
{ return m_engine->state(); }
void setState(DebuggerState state)
......@@ -90,16 +95,7 @@ protected:
void showStatusMessage(const QString &msg) const
{ m_engine->showStatusMessage(msg); }
static QString msgGdbStopFailed(const QString &why);
static QString msgInferiorStopFailed(const QString &why);
static QString msgAttachedToStoppedInferior();
static QString msgInferiorStarted();
static QString msgInferiorRunning();
static QString msgConnectRemoteServerFailed(const QString &why);
GdbEngine * const m_engine;
QProcess m_gdbProc;
};
} // namespace Internal
......
......@@ -53,7 +53,6 @@ namespace Internal {
AttachGdbAdapter::AttachGdbAdapter(GdbEngine *engine, QObject *parent)
: AbstractGdbAdapter(engine, parent)
{
commonInit();
}
void AttachGdbAdapter::startAdapter()
......@@ -62,27 +61,12 @@ void AttachGdbAdapter::startAdapter()
setState(AdapterStarting);
debugMessage(_("TRYING TO START ADAPTER"));
QStringList gdbArgs;
gdbArgs.prepend(_("mi"));
gdbArgs.prepend(_("-i"));
QString location = theDebuggerStringSetting(GdbLocation);
m_gdbProc.start(location, gdbArgs);
}
if (!m_engine->startGdb())
return;
void AttachGdbAdapter::handleGdbStarted()
{
QTC_ASSERT(state() == AdapterStarting, qDebug() << state());
emit adapterStarted();
}
void AttachGdbAdapter::handleGdbError(QProcess::ProcessError error)
{
debugMessage(_("PLAIN ADAPTER, HANDLE GDB ERROR"));
emit adapterCrashed(m_engine->errorMessage(error));
shutdown();
}
void AttachGdbAdapter::startInferior()
{
QTC_ASSERT(state() == InferiorStarting, qDebug() << state());
......@@ -113,58 +97,5 @@ void AttachGdbAdapter::interruptInferior()
debugMessage(_("CANNOT INTERRUPT %1").arg(pid));
}
void AttachGdbAdapter::shutdown()
{
switch (state()) {
case InferiorStartFailed:
m_engine->postCommand(_("-gdb-exit"));
setState(DebuggerNotReady);
return;
case InferiorStopped:
setState(InferiorShuttingDown);
m_engine->postCommand(_("detach"), CB(handleDetach));
return;
case InferiorShutDown:
setState(AdapterShuttingDown);
m_engine->postCommand(_("-gdb-exit"), GdbEngine::ExitRequest, CB(handleExit));
return;
default:
QTC_ASSERT(false, qDebug() << state());
}
}
void AttachGdbAdapter::handleDetach(const GdbResponse &response)
{
if (response.resultClass == GdbResultDone) {
setState(InferiorShutDown);
emit inferiorShutDown();
shutdown(); // re-iterate...
} else {
const QString msg = msgInferiorStopFailed(__(response.data.findChild("msg").data()));
setState(InferiorShutdownFailed);
emit inferiorShutdownFailed(msg);
}
}
void AttachGdbAdapter::handleExit(const GdbResponse &response)
{
if (response.resultClass == GdbResultDone) {
// don't set state here, this will be handled in handleGdbFinished()
} else {
const QString msg = msgGdbStopFailed(__(response.data.findChild("msg").data()));
emit adapterShutdownFailed(msg);
}
}
void AttachGdbAdapter::handleGdbFinished(int, QProcess::ExitStatus)
{
debugMessage(_("GDB PROESS FINISHED"));
emit adapterShutDown();
}
} // namespace Internal
} // namespace Debugger
......@@ -57,16 +57,10 @@ public:
void startAdapter();
void startInferior();
void interruptInferior();
void shutdown();
const char *inferiorShutdownCommand() const { return "detach"; }
private:
void handleAttach(const GdbResponse &response);
void handleDetach(const GdbResponse &response);
void handleExit(const GdbResponse &response);
Q_SLOT void handleGdbStarted();
Q_SLOT void handleGdbFinished(int, QProcess::ExitStatus);
Q_SLOT void handleGdbError(QProcess::ProcessError error);
};
} // namespace Internal
......
......@@ -53,7 +53,6 @@ namespace Internal {
CoreGdbAdapter::CoreGdbAdapter(GdbEngine *engine, QObject *parent)
: AbstractGdbAdapter(engine, parent)
{
commonInit();
}
void CoreGdbAdapter::startAdapter()
......@@ -62,27 +61,12 @@ void CoreGdbAdapter::startAdapter()
setState(AdapterStarting);
debugMessage(_("TRYING TO START ADAPTER"));
QStringList gdbArgs;
gdbArgs.prepend(_("mi"));
gdbArgs.prepend(_("-i"));
QString location = theDebuggerStringSetting(GdbLocation);
m_gdbProc.start(location, gdbArgs);
}
if (!m_engine->startGdb())
return;
void CoreGdbAdapter::handleGdbStarted()
{
QTC_ASSERT(state() == AdapterStarting, qDebug() << state());
emit adapterStarted();
}
void CoreGdbAdapter::handleGdbError(QProcess::ProcessError error)
{
debugMessage(_("PLAIN ADAPTER, HANDLE GDB ERROR"));
emit adapterCrashed(m_engine->errorMessage(error));
shutdown();
}
void CoreGdbAdapter::startInferior()
{
QTC_ASSERT(state() == InferiorStarting, qDebug() << state());
......@@ -175,45 +159,12 @@ void CoreGdbAdapter::handleTargetCore2(const GdbResponse &response)
// emit inferiorStartFailed(msg);
}
}
void CoreGdbAdapter::interruptInferior()
{
// A core should never 'run'
QTC_ASSERT(false, /**/);
}
void CoreGdbAdapter::shutdown()
{
switch (state()) {
case DebuggerNotReady:
return;
case InferiorUnrunnable:
case InferiorShutDown:
setState(AdapterShuttingDown);
m_engine->postCommand(_("-gdb-exit"), GdbEngine::ExitRequest, CB(handleExit));
return;
default:
QTC_ASSERT(false, qDebug() << state());
}
}
void CoreGdbAdapter::handleExit(const GdbResponse &response)
{
if (response.resultClass == GdbResultDone) {
// don't set state here, this will be handled in handleGdbFinished()
} else {
const QString msg = msgGdbStopFailed(__(response.data.findChild("msg").data()));
emit adapterShutdownFailed(msg);
}
}
void CoreGdbAdapter::handleGdbFinished(int, QProcess::ExitStatus)
{
debugMessage(_("GDB PROESS FINISHED"));
emit adapterShutDown();
}
} // namespace Internal
} // namespace Debugger
......@@ -57,18 +57,12 @@ public:
void startAdapter();
void startInferior();
void interruptInferior();
void shutdown();
private:
void handleTargetCore1(const GdbResponse &response);
void handleDetach1(const GdbResponse &response);
void handleFileExecAndSymbols(const GdbResponse &response);
void handleTargetCore2(const GdbResponse &response);
void handleExit(const GdbResponse &response);
Q_SLOT void handleGdbStarted();
Q_SLOT void handleGdbError(QProcess::ProcessError error);
Q_SLOT void handleGdbFinished(int, QProcess::ExitStatus);
QString m_executable;
};
......
......@@ -105,17 +105,29 @@ namespace Internal {
static bool stateAcceptsGdbCommands(DebuggerState state)
{
return state == AdapterStarted
|| state == InferiorUnrunnable
|| state == InferiorStarting
|| state == InferiorRunningRequested
|| state == InferiorRunning
|| state == InferiorStopping
|| state == InferiorStopped
|| state == InferiorShuttingDown
|| state == InferiorShutDown
|| state == AdapterShuttingDown;
};
switch (state) {
case AdapterStarting:
case AdapterStarted:
case AdapterStartFailed:
case InferiorUnrunnable:
case InferiorStarting:
case InferiorStartFailed:
case InferiorRunningRequested:
case InferiorRunning:
case InferiorStopping:
case InferiorStopped:
case InferiorShuttingDown:
case InferiorShutDown:
case InferiorShutdownFailed:
return true;
case DebuggerNotReady:
case EngineStarting:
case InferiorStopFailed:
case EngineShuttingDown:
break;
}
return false;
}
static int &currentToken()
{
......@@ -217,27 +229,13 @@ GdbEngine::~GdbEngine()