Commit 7195c6b5 authored by hjk's avatar hjk
Browse files

debugger: re-do state transitions in combined C++/Qml engine

This still needs a lot of work and sanitizing.
parent adf74394
......@@ -114,9 +114,12 @@ enum DebuggerState
InferiorSetupRequested,
InferiorSetupFailed,
InferiorSetupOk,
EngineRunRequested,
EngineRunFailed,
EngineRunOk,
InferiorUnrunnable, // Used in the core dump adapter
InferiorRunRequested, // Debuggee requested to run
......@@ -124,16 +127,19 @@ enum DebuggerState
InferiorRunFailed, // Debuggee running
InferiorStopRequested, // Debuggee running, stop requested
InferiorStopSpontaneous, // Debuggee stopped spontaneously
InferiorStopOk, // Debuggee stopped
InferiorStopFailed, // Debuggee not stopped, will kill debugger
InferiorExitOk,
InferiorShutdownRequested,
InferiorShutdownOk,
InferiorShutdownFailed,
InferiorShutdownOk,
EngineShutdownRequested,
EngineShutdownOk,
EngineShutdownFailed,
EngineShutdownOk,
DebuggerFinished
};
......
This diff is collapsed.
......@@ -241,6 +241,7 @@ public:
virtual void updateViews();
bool isSlaveEngine() const;
bool isMasterEngine() const;
DebuggerEngine *masterEngine() const;
signals:
......@@ -320,7 +321,7 @@ protected:
virtual void frameUp();
virtual void frameDown();
DebuggerRunControl *runControl() const; // FIXME: Protect.
DebuggerRunControl *runControl() const;
static QString msgWatchpointTriggered(BreakpointId id,
int number, quint64 address);
......@@ -338,12 +339,18 @@ protected:
static bool isCppBreakpoint(const Internal::BreakpointParameters &p);
bool isStateDebugging() const;
void setStateDebugging(bool on);
private:
// Wrapper engine needs access to state of its subengines.
friend class Internal::QmlCppEngine;
friend class Internal::DebuggerPluginPrivate;
void setState(DebuggerState state, bool forced = false);
virtual void setState(DebuggerState state, bool forced = false);
virtual void setSilentState(DebuggerState state);
virtual void slaveEngineStateChanged(DebuggerEngine *engine,
DebuggerState state);
friend class DebuggerEnginePrivate;
DebuggerEnginePrivate *d;
......
......@@ -141,7 +141,10 @@ static bool stateAcceptsGdbCommands(DebuggerState state)
return true;
case DebuggerNotReady:
case InferiorStopFailed:
case InferiorStopSpontaneous:
case InferiorSetupOk:
case EngineRunFailed:
case EngineRunOk:
case InferiorRunFailed:
case EngineShutdownOk:
case EngineShutdownFailed:
......
......@@ -5,9 +5,12 @@
#include "debuggercore.h"
#include <qmljseditor/qmljseditorconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <utils/qtcassert.h>
#include <QtCore/QTimer>
namespace Debugger {
......@@ -42,14 +45,12 @@ private:
DebuggerEngine *m_qmlEngine;
DebuggerEngine *m_cppEngine;
DebuggerEngine *m_activeEngine;
DebuggerState m_errorState;
};
QmlCppEnginePrivate::QmlCppEnginePrivate()
: m_qmlEngine(0),
m_cppEngine(0),
m_activeEngine(0),
m_errorState(InferiorRunOk)
m_activeEngine(0)
{}
......@@ -70,10 +71,12 @@ QmlCppEngine::QmlCppEngine(const DebuggerStartParameters &sp)
}
d->m_activeEngine = d->m_cppEngine;
connect(d->m_cppEngine, SIGNAL(stateChanged(DebuggerState)),
SLOT(slaveEngineStateChanged(DebuggerState)));
connect(d->m_qmlEngine, SIGNAL(stateChanged(DebuggerState)),
SLOT(slaveEngineStateChanged(DebuggerState)));
if (1) {
setStateDebugging(true);
d->m_cppEngine->setStateDebugging(true);
d->m_qmlEngine->setStateDebugging(true);
}
}
QmlCppEngine::~QmlCppEngine()
......@@ -217,61 +220,66 @@ void QmlCppEngine::detachDebugger()
void QmlCppEngine::executeStep()
{
notifyInferiorRunRequested();
d->m_activeEngine->executeStep();
}
void QmlCppEngine::executeStepOut()
{
notifyInferiorRunRequested();
d->m_activeEngine->executeStepOut();
}
void QmlCppEngine::executeNext()
{
notifyInferiorRunRequested();
d->m_activeEngine->executeNext();
}
void QmlCppEngine::executeStepI()
{
notifyInferiorRunRequested();
d->m_activeEngine->executeStepI();
}
void QmlCppEngine::executeNextI()
{
notifyInferiorRunRequested();
d->m_activeEngine->executeNextI();
}
void QmlCppEngine::executeReturn()
{
notifyInferiorRunRequested();
d->m_activeEngine->executeReturn();
}
void QmlCppEngine::continueInferior()
{
if (d->m_activeEngine->state() == InferiorStopOk) {
d->m_activeEngine->continueInferior();
qDebug() << "\nMASTER CONTINUE INFERIOR"
<< d->m_cppEngine->state() << d->m_qmlEngine->state();
notifyInferiorRunRequested();
if (d->m_cppEngine->state() == InferiorStopOk) {
d->m_cppEngine->continueInferior();
} else if (d->m_qmlEngine->state() == InferiorStopOk) {
d->m_qmlEngine->continueInferior();
} else {
notifyInferiorRunRequested();
QTC_ASSERT(false, qDebug() << "MASTER CANNOT CONTINUE INFERIOR"
<< d->m_cppEngine->state() << d->m_qmlEngine->state());
notifyEngineIll();
}
}
void QmlCppEngine::interruptInferior()
{
if (d->m_activeEngine->state() == InferiorRunOk) {
d->m_activeEngine->requestInterruptInferior();
} else {
if (d->m_activeEngine->state() == InferiorStopOk && (!checkErrorState(InferiorStopFailed))) {
notifyInferiorStopOk();
}
}
qDebug() << "\nMASTER INTERRUPT INFERIOR";
}
void QmlCppEngine::requestInterruptInferior()
{
qDebug() << "\nMASTER REQUEST INTERUPT INFERIOR";
DebuggerEngine::requestInterruptInferior();
if (d->m_activeEngine->state() == InferiorRunOk) {
d->m_activeEngine->requestInterruptInferior();
}
d->m_cppEngine->requestInterruptInferior();
}
void QmlCppEngine::executeRunToLine(const QString &fileName, int lineNumber)
......@@ -306,240 +314,264 @@ void QmlCppEngine::frameDown()
/////////////////////////////////////////////////////////
bool QmlCppEngine::checkErrorState(const DebuggerState stateToCheck)
void QmlCppEngine::setupEngine()
{
if (d->m_errorState != stateToCheck)
return false;
qDebug() << "\nMASTER SETUP ENGINE";
QTC_ASSERT(d->m_cppEngine->state() == DebuggerNotReady, /**/);
QTC_ASSERT(d->m_qmlEngine->state() == DebuggerNotReady, /**/);
d->m_qmlEngine->setSilentState(EngineSetupRequested);
d->m_cppEngine->setSilentState(EngineSetupRequested);
d->m_qmlEngine->setupEngine(); // Always succeeds.
d->m_cppEngine->setupEngine(); // May fail.
}
// reset state ( so that more than one error can accumulate over time )
d->m_errorState = InferiorRunOk;
switch (stateToCheck) {
case InferiorRunOk:
// nothing to do
break;
case EngineRunFailed:
notifyEngineRunFailed();
break;
case EngineSetupFailed:
notifyEngineSetupFailed();
break;
case EngineShutdownFailed:
notifyEngineShutdownFailed();
break;
case InferiorSetupFailed:
notifyInferiorSetupFailed();
break;
case InferiorRunFailed:
notifyInferiorRunFailed();
break;
case InferiorUnrunnable:
notifyInferiorUnrunnable();
break;
case InferiorStopFailed:
notifyInferiorStopFailed();
break;
case InferiorShutdownFailed:
notifyInferiorShutdownFailed();
break;
default:
// unexpected
break;
}
return true;
void QmlCppEngine::notifyEngineRunAndInferiorRunOk()
{
qDebug() << "\nMASTER NOTIFY ENGINE RUN AND INFERIOR RUN OK";
DebuggerEngine::notifyEngineRunAndInferiorRunOk();
}
void QmlCppEngine::notifyInferiorRunOk()
{
qDebug() << "\nMASTER NOTIFY INFERIOR RUN OK";
DebuggerEngine::notifyInferiorRunOk();
}
void QmlCppEngine::setupEngine()
void QmlCppEngine::notifyInferiorSpontaneousStop()
{
d->m_cppEngine->startDebugger(runControl());
qDebug() << "\nMASTER SPONTANEOUS STOP OK";
DebuggerEngine::notifyInferiorSpontaneousStop();
}
void QmlCppEngine::setupInferior()
void QmlCppEngine::notifyInferiorShutdownOk()
{
if (!checkErrorState(InferiorSetupFailed)) {
notifyInferiorSetupOk();
}
qDebug() << "\nMASTER INFERIOR SHUTDOWN OK";
DebuggerEngine::notifyInferiorShutdownOk();
}
void QmlCppEngine::runEngine()
void QmlCppEngine::setupInferior()
{
if (!checkErrorState(EngineRunFailed)) {
if (d->m_errorState == InferiorRunOk) {
switch (d->m_activeEngine->state()) {
case InferiorRunOk:
notifyEngineRunAndInferiorRunOk();
break;
case InferiorStopOk:
notifyEngineRunAndInferiorStopOk();
break;
default: // not supported?
notifyEngineRunFailed();
break;
}
} else {
notifyEngineRunFailed();
}
}
qDebug() << "\nMASTER SETUP INFERIOR";
QTC_ASSERT(d->m_cppEngine->state() == EngineSetupOk, /**/);
QTC_ASSERT(d->m_qmlEngine->state() == EngineSetupOk, /**/);
d->m_qmlEngine->setSilentState(InferiorSetupRequested);
d->m_cppEngine->setSilentState(InferiorSetupRequested);
d->m_cppEngine->setupInferior();
d->m_qmlEngine->setupInferior();
}
void QmlCppEngine::shutdownInferior()
void QmlCppEngine::runEngine()
{
if (!checkErrorState(InferiorShutdownFailed)) {
if (d->m_cppEngine->state() == InferiorStopOk) {
d->m_cppEngine->quitDebugger();
} else {
notifyInferiorShutdownOk();
}
}
qDebug() << "\nMASTER RUN ENGINE";
QTC_ASSERT(d->m_cppEngine->state() == InferiorSetupOk, /**/);
QTC_ASSERT(d->m_qmlEngine->state() == InferiorSetupOk, /**/);
d->m_qmlEngine->setSilentState(EngineRunRequested);
d->m_cppEngine->setSilentState(EngineRunRequested);
d->m_cppEngine->runEngine();
d->m_qmlEngine->runEngine();
}
void QmlCppEngine::initEngineShutdown()
void QmlCppEngine::shutdownInferior()
{
if (d->m_qmlEngine->state() != DebuggerFinished) {
d->m_qmlEngine->quitDebugger();
} else
if (d->m_cppEngine->state() != DebuggerFinished) {
d->m_cppEngine->quitDebugger();
} else
if (state() == EngineSetupRequested) {
if (!runControl() || d->m_errorState == EngineSetupFailed) {
notifyEngineSetupFailed();
} else {
notifyEngineSetupOk();
}
} else
if (state() == InferiorStopRequested) {
checkErrorState(InferiorStopFailed);
} else
if (state() == InferiorShutdownRequested && !checkErrorState(InferiorShutdownFailed)) {
notifyInferiorShutdownOk();
} else
if (state() != DebuggerFinished) {
quitDebugger();
}
qDebug() << "\nMASTER SHUTDOWN INFERIOR";
d->m_qmlEngine->quitDebugger();
}
void QmlCppEngine::shutdownEngine()
{
if (!checkErrorState(EngineShutdownFailed)) {
showStatusMessage(tr("Debugging finished"));
notifyEngineShutdownOk();
}
qDebug() << "\nMASTER SHUTDOWN ENGINE";
QTC_ASSERT(d->m_cppEngine->state() == InferiorShutdownOk, /**/);
QTC_ASSERT(d->m_qmlEngine->state() == InferiorShutdownOk, /**/);
d->m_qmlEngine->setSilentState(EngineShutdownRequested);
d->m_cppEngine->setSilentState(EngineShutdownRequested);
d->m_qmlEngine->shutdownEngine();
d->m_cppEngine->shutdownEngine();
}
void QmlCppEngine::setupSlaveEngine()
void QmlCppEngine::setState(DebuggerState newState, bool forced)
{
if (d->m_qmlEngine->state() == DebuggerNotReady)
d->m_qmlEngine->startDebugger(runControl());
qDebug() << "SET MASTER STATE: " << newState;
qDebug() << " CPP STATE: " << d->m_cppEngine->state();
qDebug() << " QML STATE: " << d->m_qmlEngine->state();
DebuggerEngine::setState(newState, forced);
}
void QmlCppEngine::slaveEngineStateChanged(const DebuggerState newState)
void QmlCppEngine::slaveEngineStateChanged
(DebuggerEngine *slaveEngine, const DebuggerState newState)
{
DebuggerEngine *slaveEngine = qobject_cast<DebuggerEngine *>(sender());
if (newState == InferiorStopOk && slaveEngine != d->m_activeEngine) {
QString engineName = slaveEngine == d->m_cppEngine
? QLatin1String("C++") : QLatin1String("QML");
showStatusMessage(tr("%1 debugger activated").arg(engineName));
d->m_activeEngine = d->m_qmlEngine;
}
const bool isCpp = slaveEngine == d->m_cppEngine;
//const bool isQml = slaveEngine == d->m_qmlEngine;
DebuggerEngine *otherEngine = isCpp ? d->m_qmlEngine : d->m_cppEngine;
qDebug() << "GOT SLAVE STATE: " << slaveEngine << newState;
qDebug() << " OTHER ENGINE: " << otherEngine << otherEngine->state();
qDebug() << " COMBINED ENGINE: " << this << state() << isDying();
switch (newState) {
case InferiorRunOk:
// startup?
if (d->m_qmlEngine->state() == DebuggerNotReady) {
setupSlaveEngine();
} else
if (d->m_cppEngine->state() == DebuggerNotReady) {
setupEngine();
} else
if (state() == EngineSetupRequested) {
case DebuggerNotReady:
case InferiorUnrunnable:
break;
case EngineSetupRequested:
break;
case EngineSetupFailed:
notifyEngineSetupFailed();
break;
case EngineSetupOk:
if (otherEngine->state() == EngineSetupOk)
notifyEngineSetupOk();
} else
// breakpoint?
if (state() == InferiorStopOk) {
continueInferior();
} else
if (state() == InferiorStopRequested) {
checkErrorState(InferiorStopFailed);
} else
if (state() == InferiorRunRequested && (!checkErrorState(InferiorRunFailed)) && (!checkErrorState(InferiorUnrunnable))) {
notifyInferiorRunOk();
else
qDebug() << "... WAITING FOR OTHER ENGINE SETUP...";
break;
case InferiorSetupRequested:
break;
case InferiorSetupFailed:
notifyInferiorSetupFailed();
break;
case InferiorSetupOk:
if (otherEngine->state() == InferiorSetupOk)
notifyInferiorSetupOk();
else
qDebug() << "... WAITING FOR OTHER INFERIOR SETUP...";
break;
case EngineRunRequested:
break;
case EngineRunFailed:
notifyEngineRunFailed();
break;
case EngineRunOk:
if (otherEngine->state() == EngineRunOk) {
// This is conditionalized on isMasterEngine() in the
// base class, so do it here manually.
slaveEngine->setSilentState(InferiorRunOk);
otherEngine->setSilentState(InferiorRunOk);
notifyEngineRunAndInferiorRunOk();
} else {
qDebug() << "... WAITING FOR OTHER ENGINE RUN...";
}
break;
case InferiorRunRequested:
// follow the inferior
if (state() == InferiorStopOk && checkErrorState(InferiorRunOk)) {
continueInferior();
break;
case InferiorRunFailed:
notifyInferiorRunFailed();
break;
case InferiorRunOk:
qDebug() << "PLANNED INFERIOR RUN";
if (otherEngine->state() == InferiorRunOk)
notifyInferiorRunOk();
else
qDebug() << " **** INFERIOR RUN NOT OK ****";
break;
case InferiorStopSpontaneous:
notifyInferiorSpontaneousStop();
slaveEngine->setSilentState(InferiorStopOk);
if (slaveEngine != d->m_activeEngine) {
QString engineName = slaveEngine == d->m_cppEngine
? QLatin1String("C++") : QLatin1String("QML");
showStatusMessage(tr("%1 debugger activated").arg(engineName));
d->m_activeEngine = slaveEngine;
}
break;
case InferiorStopRequested:
// follow the inferior
if (state() == InferiorRunOk && checkErrorState(InferiorRunOk)) {
requestInterruptInferior();
}
break;
case InferiorStopFailed:
notifyInferiorStopFailed();
break;
case InferiorStopOk:
// check breakpoints
if (state() == InferiorRunRequested) {
checkErrorState(InferiorRunFailed);
} else
if (checkErrorState(InferiorRunOk)) {
if (state() == InferiorRunOk) {
requestInterruptInferior();
} else
if (state() == InferiorStopRequested) {
interruptInferior();
if (isDying()) {
qDebug() << "... AN INFERIOR STOPPED DURING SHUTDOWN ";
} else {
if (otherEngine->state() == InferiorShutdownOk) {
qDebug() << "... STOPP ";
} else if (state() == InferiorStopRequested) {
qDebug() << "... AN INFERIOR STOPPED EXPECTEDLY";
notifyInferiorStopOk();
} else {
qDebug() << "... AN INFERIOR STOPPED UNEXPECTEDLY";
notifyInferiorSpontaneousStop();
}
}
break;
case EngineRunFailed:
case EngineSetupFailed:
case EngineShutdownFailed:
case InferiorSetupFailed:
case InferiorRunFailed:
case InferiorUnrunnable:
case InferiorStopFailed:
case InferiorShutdownFailed:
if (d->m_errorState == InferiorRunOk) {
d->m_errorState = newState;
case InferiorExitOk:
slaveEngine->setSilentState(InferiorShutdownOk);
if (otherEngine->state() == InferiorShutdownOk) {
notifyInferiorExited();
} else {
if (state() == InferiorRunOk)
notifyInferiorSpontaneousStop();
otherEngine->notifyInferiorExited();
}
break;
case InferiorShutdownRequested:
break;
case InferiorShutdownFailed:
notifyInferiorShutdownFailed();
break;
case InferiorShutdownOk:
if (otherEngine->state() == InferiorShutdownOk)
notifyInferiorShutdownOk();
else if (otherEngine->state() == InferiorRunOk)
otherEngine->quitDebugger();
else if (otherEngine->state() == InferiorStopOk)
otherEngine->quitDebugger();
break;
case EngineShutdownRequested:
// we have to abort the setup before the sub-engines die
// because we depend on an active runcontrol that will be shut down by the dying engine
if (state() == EngineSetupRequested)