diff --git a/debuggers/common/midebugsession.h b/debuggers/common/midebugsession.h --- a/debuggers/common/midebugsession.h +++ b/debuggers/common/midebugsession.h @@ -35,6 +35,8 @@ #include "mi/mi.h" #include "mi/micommand.h" +#include + #include class IExecutePlugin; @@ -50,6 +52,7 @@ } class MIDebugger; +class MIVariable; class STTY; class MIDebugSession : public KDevelop::IDebugSession { @@ -211,6 +214,10 @@ void (Handler::* handler_method)(const MI::ResultRecord&), MI::CommandFlags flags = 0); + QMap & variableMapping(); + MIVariable* findVariableByVarobjName(const QString &varobjName); + void markAllVariableDead(); + protected Q_SLOTS: virtual void slotDebuggerReady(); virtual void slotDebuggerExited(bool abnormal, const QString &msg); @@ -327,6 +334,9 @@ bool m_hasCrashed; bool m_sourceInitFile; + + // Map from GDB varobj name to MIVariable. + QMap m_allVariables; }; template diff --git a/debuggers/common/midebugsession.cpp b/debuggers/common/midebugsession.cpp --- a/debuggers/common/midebugsession.cpp +++ b/debuggers/common/midebugsession.cpp @@ -30,6 +30,7 @@ #include "debuglog.h" #include "midebugger.h" +#include "mivariable.h" #include "mi/mi.h" #include "mi/micommand.h" #include "mi/micommandqueue.h" @@ -114,6 +115,27 @@ return m_sessionState; } +QMap & MIDebugSession::variableMapping() +{ + return m_allVariables; +} + +MIVariable* MIDebugSession::findVariableByVarobjName(const QString &varobjName) +{ + if (m_allVariables.count(varobjName) == 0) + return nullptr; + return m_allVariables[varobjName]; +} + +void MIDebugSession::markAllVariableDead() +{ + for (auto i = m_allVariables.begin(), e = m_allVariables.end(); i != e; ++i) + { + i.value()->markAsDead(); + } + m_allVariables.clear(); +} + bool MIDebugSession::restartAvaliable() const { if (debuggerStateIsOn(s_attached) || debuggerStateIsOn(s_core)) { diff --git a/debuggers/common/mivariable.h b/debuggers/common/mivariable.h --- a/debuggers/common/mivariable.h +++ b/debuggers/common/mivariable.h @@ -27,16 +27,17 @@ #include #include +#include class CreateVarobjHandler; class FetchMoreChildrenHandler; namespace KDevMI { - +class MIDebugSession; class MIVariable : public KDevelop::Variable { public: - MIVariable(KDevelop::TreeModel* model, KDevelop::TreeItem* parent, + MIVariable(MIDebugSession *session, KDevelop::TreeModel* model, KDevelop::TreeItem* parent, const QString& expression, const QString& display = ""); ~MIVariable(); @@ -46,11 +47,9 @@ const QString& varobj() const; void handleUpdate(const MI::Value& var); - static MIVariable *findByVarobjName(const QString& varobjName); - /* Called when debugger dies. Clears the association between varobj names and Variable instances. */ - static void markAllDead(); + void markAsDead(); bool canSetFormat() const override { return true; } @@ -62,17 +61,19 @@ private: // Internal friend class ::CreateVarobjHandler; friend class ::FetchMoreChildrenHandler; + QString enquotedExpression() const; + + bool sessionIsAlive() const; + void setVarobj(const QString& v); QString varobj_; + QPointer debugSession; + // How many children should be fetched in one // increment. static const int fetchStep = 5; - - /* Map from GDB varobj name to GdbVariable. - FIXME: eventually, should be per-session map. */ - static QMap allVariables_; }; } // end of KDevMI diff --git a/debuggers/common/mivariable.cpp b/debuggers/common/mivariable.cpp --- a/debuggers/common/mivariable.cpp +++ b/debuggers/common/mivariable.cpp @@ -21,6 +21,7 @@ #include "mivariable.h" +#include "debuglog.h" #include "midebugsession.h" #include "mi/micommand.h" @@ -31,24 +32,20 @@ using namespace KDevMI; using namespace KDevMI::MI; -QMap MIVariable::allVariables_; - -static bool hasStartedSession() +bool MIVariable::sessionIsAlive() const { - if (!ICore::self()->debugController()) return false; //happens on shutdown - - IDebugSession *session = ICore::self()->debugController()->currentSession(); - if (!session) + if (!debugSession) return false; - IDebugSession::DebuggerState s = session->state(); + IDebugSession::DebuggerState s = debugSession->state(); return s != IDebugSession::NotStartedState && s != IDebugSession::EndedState; } -MIVariable::MIVariable(TreeModel* model, TreeItem* parent, +MIVariable::MIVariable(MIDebugSession *session, TreeModel* model, TreeItem* parent, const QString& expression, const QString& display) : Variable(model, parent, expression, display) + , debugSession(session) { } @@ -58,33 +55,29 @@ { // Delete only top-level variable objects. if (topLevel()) { - if (hasStartedSession()) { - IDebugSession* is = ICore::self()->debugController()->currentSession(); - MIDebugSession * s = static_cast(is); - s->addCommand(VarDelete, QString("\"%1\"").arg(varobj_)); + if (sessionIsAlive()) { + debugSession->addCommand(VarDelete, QString("\"%1\"").arg(varobj_)); } } - allVariables_.remove(varobj_); + if (debugSession) + debugSession->variableMapping().remove(varobj_); } } -MIVariable* MIVariable::findByVarobjName(const QString& varobjName) -{ - if (allVariables_.count(varobjName) == 0) - return 0; - return allVariables_[varobjName]; -} - void MIVariable::setVarobj(const QString& v) { + if (!debugSession) { + qCDebug(DEBUGGERCOMMON) << "WARNING: MIVariable::setVarobj called when its session died"; + return; + } if (!varobj_.isEmpty()) { // this should not happen // but apperently it does when attachMaybe is called a second time before // the first -var-create call returned - allVariables_.remove(varobj_); + debugSession->variableMapping().remove(varobj_); } varobj_ = v; - allVariables_[varobj_] = this; + debugSession->variableMapping()[varobj_] = this; } @@ -153,23 +146,20 @@ if (!varobj_.isEmpty()) return; - if (hasStartedSession()) { - IDebugSession* is = ICore::self()->debugController()->currentSession(); - MIDebugSession * s = static_cast(is); - s->addCommand(VarCreate, - QString("var%1 @ %2").arg(nextId++).arg(enquotedExpression()), - new CreateVarobjHandler(this, callback, callbackMethod)); + // Try find a current session and attach to it + if (!ICore::self()->debugController()) return; //happens on shutdown + debugSession = static_cast(ICore::self()->debugController()->currentSession()); + + if (sessionIsAlive()) { + debugSession->addCommand(VarCreate, + QString("var%1 @ %2").arg(nextId++).arg(enquotedExpression()), + new CreateVarobjHandler(this, callback, callbackMethod)); } } -void MIVariable::markAllDead() +void MIVariable::markAsDead() { - QMap::iterator i, e; - for (i = allVariables_.begin(), e = allVariables_.end(); i != e; ++i) - { - i.value()->varobj_.clear(); - } - allVariables_.clear(); + varobj_.clear(); } class FetchMoreChildrenHandler : public MICommandHandler @@ -247,13 +237,12 @@ int c = childItems.size(); // FIXME: should not even try this if app is not started. // Probably need to disable open, or something - if (hasStartedSession()) { - IDebugSession* is = ICore::self()->debugController()->currentSession(); - MIDebugSession * s = static_cast(is); - s->addCommand(VarListChildren, - QString("--all-values \"%1\" %2 %3") - .arg(varobj_).arg( c ).arg( c + fetchStep ), // fetch from .. to .. - new FetchMoreChildrenHandler(this, s)); + if (sessionIsAlive()) { + debugSession->addCommand(VarListChildren, + QString("--all-values \"%1\" %2 %3") + // fetch from .. to .. + .arg(varobj_).arg(c).arg(c + fetchStep), + new FetchMoreChildrenHandler(this, debugSession)); } } @@ -298,19 +287,18 @@ const Value& child = children[i]; const QString& exp = child["exp"].literal(); - IDebugSession* is = ICore::self()->debugController()->currentSession(); - MIDebugSession * s = static_cast(is); - KDevelop::Variable* xvar = s->variableController()-> - createVariable(model(), this, exp); - MIVariable* var = static_cast(xvar); - var->setTopLevel(false); - var->setVarobj(child["name"].literal()); - bool hasMore = child["numchild"].toInt() != 0 || ( child.hasField("dynamic") && child["dynamic"].toInt()!=0 ); - var->setHasMoreInitial(hasMore); - appendChild(var); - var->setType(child["type"].literal()); - var->setValue(child["value"].literal()); - var->setChanged(true); + if (debugSession) { + auto xvar = debugSession->variableController()->createVariable(model(), this, exp); + auto var = static_cast(xvar); + var->setTopLevel(false); + var->setVarobj(child["name"].literal()); + bool hasMore = child["numchild"].toInt() != 0 || ( child.hasField("dynamic") && child["dynamic"].toInt()!=0 ); + var->setHasMoreInitial(hasMore); + appendChild(var); + var->setType(child["type"].literal()); + var->setValue(child["value"].literal()); + var->setChanged(true); + } } } @@ -365,12 +353,10 @@ } else { - if (hasStartedSession()) { - IDebugSession* is = ICore::self()->debugController()->currentSession(); - MIDebugSession * s = static_cast(is); - s->addCommand(VarSetFormat, - QString(" \"%1\" %2 ").arg(varobj_).arg(format2str(format())), - new SetFormatHandler(this)); + if (sessionIsAlive()) { + debugSession->addCommand(VarSetFormat, + QString(" \"%1\" %2 ").arg(varobj_).arg(format2str(format())), + new SetFormatHandler(this)); } } } diff --git a/debuggers/common/mivariablecontroller.cpp b/debuggers/common/mivariablecontroller.cpp --- a/debuggers/common/mivariablecontroller.cpp +++ b/debuggers/common/mivariablecontroller.cpp @@ -98,7 +98,7 @@ for (int i = 0; i < changed.size(); ++i) { const Value& var = changed[i]; - MIVariable* v = MIVariable::findByVarobjName(var["name"].literal()); + MIVariable* v = debugSession()->findVariableByVarobjName(var["name"].literal()); // v can be NULL here if we've already removed locals after step, // but the corresponding -var-delete command is still in the queue. if (v) { @@ -239,7 +239,7 @@ Variable* MIVariableController::createVariable(TreeModel* model, TreeItem* parent, const QString& expression, const QString& display) { - return new MIVariable(model, parent, expression, display); + return new MIVariable(debugSession(), model, parent, expression, display); } void MIVariableController::handleEvent(IDebugSession::event_t event) @@ -250,6 +250,6 @@ void MIVariableController::stateChanged(IDebugSession::DebuggerState state) { if (state == IDebugSession::EndedState) { - MIVariable::markAllDead(); + debugSession()->markAllVariableDead(); } } diff --git a/debuggers/gdb/gdbvariable.h b/debuggers/gdb/gdbvariable.h --- a/debuggers/gdb/gdbvariable.h +++ b/debuggers/gdb/gdbvariable.h @@ -31,13 +31,13 @@ } namespace KDevMI { namespace GDB { - +class DebugSession; class GdbVariable : public KDevMI::MIVariable { Q_OBJECT public: - GdbVariable(KDevelop::TreeModel* model, KDevelop::TreeItem* parent, + GdbVariable(DebugSession *session, KDevelop::TreeModel* model, KDevelop::TreeItem* parent, const QString& expression, const QString& display = ""); }; diff --git a/debuggers/gdb/gdbvariable.cpp b/debuggers/gdb/gdbvariable.cpp --- a/debuggers/gdb/gdbvariable.cpp +++ b/debuggers/gdb/gdbvariable.cpp @@ -22,11 +22,13 @@ #include "gdbvariable.h" +#include "debugsession.h" + using namespace KDevelop; using namespace KDevMI::GDB; -GdbVariable::GdbVariable(TreeModel *model, TreeItem *parent, +GdbVariable::GdbVariable(DebugSession *session, TreeModel *model, TreeItem *parent, const QString& expression, const QString& display) - : MIVariable(model, parent, expression, display) + : MIVariable(session, model, parent, expression, display) { } diff --git a/debuggers/gdb/unittests/test_gdb.cpp b/debuggers/gdb/unittests/test_gdb.cpp --- a/debuggers/gdb/unittests/test_gdb.cpp +++ b/debuggers/gdb/unittests/test_gdb.cpp @@ -1261,24 +1261,24 @@ void GdbTest::testVariablesStartSecondSession() { - TestDebugSession *session = new TestDebugSession; + QPointer session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); - session = new TestDebugSession; - session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); + QPointer session2 = new TestDebugSession; + session2->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); - QVERIFY(session->startDebugging(&cfg, m_iface)); - WAIT_FOR_STATE(session, DebugSession::PausedState); + QVERIFY(session2->startDebugging(&cfg, m_iface)); + WAIT_FOR_STATE(session2, DebugSession::PausedState); - session->run(); - WAIT_FOR_STATE(session, DebugSession::EndedState); + session2->run(); + WAIT_FOR_STATE(session2, DebugSession::EndedState); } void GdbTest::testVariablesSwitchFrame()