File indexing completed on 2024-04-28 04:38:36
0001 /* 0002 SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org> 0003 SPDX-FileCopyrightText: 2008 Vladimir Prus <ghost@cs.msu.su> 0004 SPDX-FileCopyrightText: 2009 Niko Sams <niko.sams@gmail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "mivariablecontroller.h" 0010 0011 #include "debuglog.h" 0012 #include "midebugsession.h" 0013 #include "mivariable.h" 0014 #include "mi/mi.h" 0015 #include "mi/micommand.h" 0016 #include "stringhelpers.h" 0017 0018 #include <debugger/interfaces/iframestackmodel.h> 0019 #include <debugger/breakpoint/breakpointmodel.h> 0020 #include <debugger/variable/variablecollection.h> 0021 #include <interfaces/icore.h> 0022 #include <interfaces/idebugcontroller.h> 0023 0024 #include <KTextEditor/Document> 0025 0026 using namespace KDevelop; 0027 using namespace KDevMI; 0028 using namespace KDevMI::MI; 0029 using KTextEditor::Cursor; 0030 using KTextEditor::Document; 0031 using KTextEditor::Range; 0032 0033 MIVariableController::MIVariableController(MIDebugSession *parent) 0034 : IVariableController(parent) 0035 { 0036 Q_ASSERT(parent); 0037 connect(parent, &MIDebugSession::inferiorStopped, 0038 this, &MIVariableController::programStopped); 0039 connect(parent, &MIDebugSession::stateChanged, 0040 this, &MIVariableController::stateChanged); 0041 } 0042 0043 MIDebugSession *MIVariableController::debugSession() const 0044 { 0045 return static_cast<MIDebugSession *>(const_cast<QObject*>(QObject::parent())); 0046 } 0047 0048 void MIVariableController::programStopped(const AsyncRecord& r) 0049 { 0050 if (debugSession()->debuggerStateIsOn(s_shuttingDown)) return; 0051 0052 if (r.hasField(QStringLiteral("reason")) && r[QStringLiteral("reason")].literal() == QLatin1String("function-finished") 0053 && r.hasField(QStringLiteral("gdb-result-var"))) 0054 { 0055 variableCollection()->watches()->addFinishResult(r[QStringLiteral("gdb-result-var")].literal()); 0056 } else { 0057 variableCollection()->watches()->removeFinishResult(); 0058 } 0059 } 0060 0061 void MIVariableController::update() 0062 { 0063 qCDebug(DEBUGGERCOMMON) << "autoUpdate =" << autoUpdate(); 0064 if (autoUpdate() & UpdateWatches) { 0065 variableCollection()->watches()->reinstall(); 0066 } 0067 0068 if (autoUpdate() & UpdateLocals) { 0069 updateLocals(); 0070 } 0071 0072 if ((autoUpdate() & UpdateLocals) || 0073 ((autoUpdate() & UpdateWatches) && variableCollection()->watches()->childCount() > 0)) 0074 { 0075 debugSession()->addCommand(VarUpdate, QStringLiteral("--all-values *"), this, 0076 &MIVariableController::handleVarUpdate); 0077 } 0078 } 0079 0080 void MIVariableController::handleVarUpdate(const ResultRecord& r) 0081 { 0082 const Value& changed = r[QStringLiteral("changelist")]; 0083 for (int i = 0; i < changed.size(); ++i) 0084 { 0085 const Value& var = changed[i]; 0086 MIVariable* v = debugSession()->findVariableByVarobjName(var[QStringLiteral("name")].literal()); 0087 // v can be NULL here if we've already removed locals after step, 0088 // but the corresponding -var-delete command is still in the queue. 0089 if (v) { 0090 v->handleUpdate(var); 0091 } 0092 } 0093 } 0094 0095 class StackListArgumentsHandler : public MICommandHandler 0096 { 0097 public: 0098 explicit StackListArgumentsHandler(const QStringList& localsName) 0099 : m_localsName(localsName) 0100 {} 0101 0102 void handle(const ResultRecord &r) override 0103 { 0104 if (!KDevelop::ICore::self()->debugController()) return; //happens on shutdown 0105 0106 if (r.hasField(QStringLiteral("stack-args")) && r[QStringLiteral("stack-args")].size() > 0) { 0107 const Value& locals = r[QStringLiteral("stack-args")][0][QStringLiteral("args")]; 0108 0109 m_localsName.reserve(m_localsName.size() + locals.size()); 0110 for (int i = 0; i < locals.size(); i++) { 0111 m_localsName << locals[i].literal(); 0112 } 0113 const QList<Variable*> variables = KDevelop::ICore::self()->debugController()->variableCollection() 0114 ->locals()->updateLocals(m_localsName); 0115 for (Variable* v : variables) { 0116 v->attachMaybe(); 0117 } 0118 } 0119 } 0120 0121 private: 0122 QStringList m_localsName; 0123 }; 0124 0125 class StackListLocalsHandler : public MICommandHandler 0126 { 0127 public: 0128 explicit StackListLocalsHandler(MIDebugSession *session) 0129 : m_session(session) 0130 {} 0131 0132 void handle(const ResultRecord &r) override 0133 { 0134 if (r.hasField(QStringLiteral("locals"))) { 0135 const Value& locals = r[QStringLiteral("locals")]; 0136 0137 QStringList localsName; 0138 localsName.reserve(locals.size()); 0139 for (int i = 0; i < locals.size(); i++) { 0140 const Value& var = locals[i]; 0141 localsName << var[QStringLiteral("name")].literal(); 0142 } 0143 int frame = m_session->frameStackModel()->currentFrame(); 0144 m_session->addCommand(StackListArguments, 0145 // do not show value, low-frame, high-frame 0146 QStringLiteral("0 %1 %2").arg(frame).arg(frame), 0147 new StackListArgumentsHandler(localsName)); 0148 } 0149 } 0150 0151 private: 0152 MIDebugSession *m_session; 0153 }; 0154 0155 void MIVariableController::updateLocals() 0156 { 0157 debugSession()->addCommand(StackListLocals, QStringLiteral("--simple-values"), 0158 new StackListLocalsHandler(debugSession())); 0159 } 0160 0161 Range MIVariableController::expressionRangeUnderCursor(Document* doc, const Cursor& cursor) 0162 { 0163 const QString line = doc->line(cursor.line()); 0164 int index = cursor.column(); 0165 if (index >= line.size()) { 0166 return {}; 0167 } 0168 QChar c = line[index]; 0169 if (!c.isLetterOrNumber() && c != QLatin1Char('_')) { 0170 return {}; 0171 } 0172 0173 int start = Utils::expressionAt(line, index+1); 0174 int end = index; 0175 for (; end < line.size(); ++end) 0176 { 0177 QChar c = line[end]; 0178 if (!(c.isLetterOrNumber() || c == QLatin1Char('_'))) { 0179 break; 0180 } 0181 } 0182 if (!(start < end)) 0183 return {}; 0184 0185 return { Cursor{cursor.line(), start}, Cursor{cursor.line(), end} }; 0186 } 0187 0188 0189 void MIVariableController::addWatch(KDevelop::Variable* variable) 0190 { 0191 // FIXME: should add async 'get full expression' method 0192 // to MIVariable, not poke at varobj. In that case, 0193 // we will be able to make addWatch a generic method, not 0194 // gdb-specific one. 0195 if (auto* gv = qobject_cast<MIVariable*>(variable)) { 0196 debugSession()->addCommand(VarInfoPathExpression, 0197 gv->varobj(), 0198 this, &MIVariableController::addWatch); 0199 } 0200 } 0201 0202 void MIVariableController::addWatchpoint(KDevelop::Variable* variable) 0203 { 0204 // FIXME: should add async 'get full expression' method 0205 // to MIVariable, not poke at varobj. In that case, 0206 // we will be able to make addWatchpoint a generic method, not 0207 // gdb-specific one. 0208 if (auto* gv = qobject_cast<MIVariable*>(variable)) { 0209 debugSession()->addCommand(VarInfoPathExpression, 0210 gv->varobj(), 0211 this, &MIVariableController::addWatchpoint); 0212 } 0213 } 0214 0215 void MIVariableController::addWatch(const ResultRecord& r) 0216 { 0217 if (r.reason == QLatin1String("done") 0218 && r.hasField(QStringLiteral("path_expr")) 0219 && !r[QStringLiteral("path_expr")].literal().isEmpty()) { 0220 variableCollection()->watches()->add(r[QStringLiteral("path_expr")].literal()); 0221 } 0222 } 0223 0224 void MIVariableController::addWatchpoint(const ResultRecord& r) 0225 { 0226 if (r.reason == QLatin1String("done") && !r[QStringLiteral("path_expr")].literal().isEmpty()) { 0227 KDevelop::ICore::self()->debugController()->breakpointModel()->addWatchpoint(r[QStringLiteral("path_expr")].literal()); 0228 } 0229 } 0230 0231 Variable* MIVariableController::createVariable(TreeModel* model, TreeItem* parent, 0232 const QString& expression, const QString& display) 0233 { 0234 return new MIVariable(debugSession(), model, parent, expression, display); 0235 } 0236 0237 void MIVariableController::handleEvent(IDebugSession::event_t event) 0238 { 0239 IVariableController::handleEvent(event); 0240 } 0241 0242 void MIVariableController::stateChanged(IDebugSession::DebuggerState state) 0243 { 0244 if (state == IDebugSession::EndedState) { 0245 debugSession()->markAllVariableDead(); 0246 } 0247 } 0248 0249 #include "moc_mivariablecontroller.cpp"