diff --git a/src/plugins/debugger/dap/cmakedapengine.cpp b/src/plugins/debugger/dap/cmakedapengine.cpp index a2e38fd09a1..d01f4488417 100644 --- a/src/plugins/debugger/dap/cmakedapengine.cpp +++ b/src/plugins/debugger/dap/cmakedapengine.cpp @@ -147,6 +147,7 @@ bool CMakeDapEngine::hasCapability(unsigned cap) const return cap & (ReloadModuleCapability | BreakConditionCapability | ShowModuleSymbolsCapability + /*| AddWatcherCapability*/ // disable while the #25282 bug is not fixed /*| RunToLineCapability*/); // disable while the #25176 bug is not fixed } diff --git a/src/plugins/debugger/dap/dapclient.cpp b/src/plugins/debugger/dap/dapclient.cpp index 80fadd19ce3..f63d45b2bb9 100644 --- a/src/plugins/debugger/dap/dapclient.cpp +++ b/src/plugins/debugger/dap/dapclient.cpp @@ -114,11 +114,19 @@ void DapClient::sendStepOver(int threadId) postRequest("next", QJsonObject{{"threadId", threadId}}); } +void DapClient::evaluateVariable(const QString &expression, int frameId) +{ + postRequest("evaluate", + QJsonObject{{"expression", expression}, + {"frameId", frameId}, + {"context", "variables"}}); +} + void DapClient::stackTrace(int threadId) { QTC_ASSERT(threadId != -1, return); postRequest("stackTrace", - QJsonObject{{"threadId", threadId}, {"startFrame", 0}, {"levels", 10}}); + QJsonObject{{"threadId", threadId}, {"startFrame", 0}, {"levels", 10}}); } void DapClient::scopes(int frameId) @@ -139,8 +147,8 @@ void DapClient::variables(int variablesReference) void DapClient::setBreakpoints(const QJsonArray &breakpoints, const FilePath &fileName) { postRequest("setBreakpoints", - QJsonObject{{"source", QJsonObject{{"path", fileName.path()}}}, - {"breakpoints", breakpoints}}); + QJsonObject{{"source", QJsonObject{{"path", fileName.path()}}}, + {"breakpoints", breakpoints}}); } void DapClient::readOutput() @@ -214,6 +222,8 @@ void DapClient::emitSignals(const QJsonDocument &doc) type = DapResponseType::DapThreads; } else if (command == "pause") { type = DapResponseType::Pause; + } else if (command == "evaluate") { + type = DapResponseType::Evaluate; } emit responseReady(type, ob); return; diff --git a/src/plugins/debugger/dap/dapclient.h b/src/plugins/debugger/dap/dapclient.h index 066abd3fd53..d2a6f52bb22 100644 --- a/src/plugins/debugger/dap/dapclient.h +++ b/src/plugins/debugger/dap/dapclient.h @@ -51,6 +51,7 @@ enum class DapResponseType StepOut, StepOver, Pause, + Evaluate, Unknown }; @@ -93,11 +94,14 @@ public: void sendStepOut(int threadId); void sendStepOver(int threadId); + void evaluateVariable(const QString &expression, int frameId); + void stackTrace(int threadId); void scopes(int frameId); void threads(); void variables(int variablesReference); void setBreakpoints(const QJsonArray &breakpoints, const Utils::FilePath &fileName); + void emitSignals(const QJsonDocument &doc); signals: diff --git a/src/plugins/debugger/dap/dapengine.cpp b/src/plugins/debugger/dap/dapengine.cpp index 8df09b0d0d6..d722c14019c 100644 --- a/src/plugins/debugger/dap/dapengine.cpp +++ b/src/plugins/debugger/dap/dapengine.cpp @@ -63,6 +63,79 @@ using namespace Utils; namespace Debugger::Internal { +VariablesHandler::VariablesHandler(DapEngine *dapEngine) + : m_dapEngine(dapEngine) +{} + +void VariablesHandler::addVariable(const QString &iname, int variablesReference) +{ + VariableItem varItem = {iname, variablesReference}; + bool wasEmpty = m_queue.empty(); + bool inserted = false; + + for (auto i = m_queue.begin(); i != m_queue.end(); ++i) { + if (i->iname > iname) { + m_queue.insert(i, varItem); + inserted = true; + break; + } + } + if (!inserted) + m_queue.push_back(varItem); + + if (wasEmpty) { + startHandling(); + } +} + +void VariablesHandler::handleNext() +{ + if (m_queue.empty()) + return; + + m_queue.pop_front(); + startHandling(); +} + +void VariablesHandler::startHandling() +{ + if (m_queue.empty()) + return; + + m_currentVarItem = m_queue.front(); + + WatchItem *watchItem = m_dapEngine->watchHandler()->findItem(m_currentVarItem.iname); + int variablesReference = m_currentVarItem.variablesReference; + + if (variablesReference == -1 && watchItem && watchItem->iname.startsWith("watch.") + && watchItem->iname.split('.').size() == 2) { + watchItem->removeChildren(); + m_dapEngine->dapClient()->evaluateVariable(watchItem->name, + m_dapEngine->currentStackFrameId()); + return; + } + + if (variablesReference == -1) { + if (watchItem) { + variablesReference = watchItem->variablesReference; + } else { + handleNext(); + return; + } + } + + if (variablesReference == 0) { + handleNext(); + return; + } + + m_dapEngine->dapClient()->variables(variablesReference); +} + +DapEngine::DapEngine() + : m_variablesHandler(std::make_unique(this)) +{} + void DapEngine::executeDebuggerCommand(const QString &/*command*/) { QTC_ASSERT(state() == InferiorStopOk, qCDebug(logCategory()) << state()); @@ -350,13 +423,6 @@ void DapEngine::refreshModules(const GdbMi &modules) handler->endUpdateAll(); } -void DapEngine::requestModuleSymbols(const Utils::FilePath &/*moduleName*/) -{ -// DebuggerCommand cmd("listSymbols"); -// cmd.arg("module", moduleName); -// runCommand(cmd); -} - void DapEngine::refreshState(const GdbMi &reportedState) { QString newState = reportedState.data(); @@ -399,50 +465,32 @@ bool DapEngine::canHandleToolTip(const DebuggerToolTipContext &) const return state() == InferiorStopOk; } -void DapEngine::assignValueInDebugger(WatchItem *, const QString &/*expression*/, const QVariant &/*value*/) -{ - //DebuggerCommand cmd("assignValue"); - //cmd.arg("expression", expression); - //cmd.arg("value", value.toString()); - //runCommand(cmd); - // postDirectCommand("global " + expression + ';' + expression + "=" + value.toString()); - updateLocals(); -} - void DapEngine::updateItem(const QString &iname) { WatchItem *item = watchHandler()->findItem(iname); - if (item && m_currentWatchItem != item) { - m_currentWatchItem = item; - m_dapClient->variables(item->variablesReference); - } -} -void DapEngine::getVariableFromQueue() -{ - WatchItem *item = nullptr; - while (!item && !m_variablesReferenceInameQueue.empty()) { - item = watchHandler()->findItem(m_variablesReferenceInameQueue.front()); - m_variablesReferenceInameQueue.pop(); - } - - if (item) { - m_currentWatchItem = item; - m_dapClient->variables(item->variablesReference); - } + if (item && m_variablesHandler->currentItem().iname != item->iname) + m_variablesHandler->addVariable(item->iname, item->variablesReference); } void DapEngine::reexpandItems(const QSet &inames) { - QList inamesVector = inames.values().toVector(); + QSet expandedInames = inames; + for (auto inames : watchHandler()->watcherNames().keys()) + expandedInames.insert(watchHandler()->watcherName(inames)); + + QList inamesVector = expandedInames.values().toVector(); inamesVector.sort(); for (const QString &iname : inamesVector) { - if (iname.startsWith("local.")) - m_variablesReferenceInameQueue.push(iname); + if (iname.startsWith("local.") || iname.startsWith("watch.")) + m_variablesHandler->addVariable(iname, -1); } +} - getVariableFromQueue(); +void DapEngine::doUpdateLocals(const UpdateParameters ¶ms) +{ + m_variablesHandler->addVariable(params.partialVariable, -1); } QString DapEngine::errorMessage(QProcess::ProcessError error) const @@ -508,12 +556,6 @@ void DapEngine::handleResponse(DapResponseType type, const QJsonObject &response { const QString command = response.value("command").toString(); - if (response.contains("success") && !response.value("success").toBool()) { - showMessage(QString("DAP COMMAND FAILED: %1").arg(command)); - qCDebug(logCategory()) << "DAP COMMAND FAILED:" << command; - return; - } - switch (type) { case DapResponseType::Initialize: qCDebug(logCategory()) << "initialize success"; @@ -553,9 +595,18 @@ void DapEngine::handleResponse(DapResponseType type, const QJsonObject &response case DapResponseType::DapThreads: handleThreadsResponse(response); break; + case DapResponseType::Evaluate: + handleEvaluateResponse(response); + break; default: showMessage("UNKNOWN RESPONSE:" + command); }; + + if (response.contains("success") && !response.value("success").toBool()) { + showMessage(QString("DAP COMMAND FAILED: %1").arg(command)); + qCDebug(logCategory()) << "DAP COMMAND FAILED:" << command; + return; + } } void DapEngine::handleStackTraceResponse(const QJsonObject &response) @@ -581,24 +632,17 @@ void DapEngine::handleScopesResponse(const QJsonObject &response) if (!response.value("success").toBool()) return; - watchHandler()->removeAllData(); watchHandler()->notifyUpdateStarted(); - m_watchItems.clear(); - QJsonArray scopes = response.value("body").toObject().value("scopes").toArray(); for (const QJsonValueRef &scope : scopes) { const QString name = scope.toObject().value("name").toString(); if (name == "Registers") continue; - m_variablesReferenceQueue.push(scope.toObject().value("variablesReference").toInt()); + m_variablesHandler->addVariable("", scope.toObject().value("variablesReference").toInt()); } - if (!m_variablesReferenceQueue.empty()) { - m_isFirstLayer = true; - m_dapClient->variables(m_variablesReferenceQueue.front()); - m_variablesReferenceQueue.pop(); - } else { + if (m_variablesHandler->queueSize() == 0) { watchHandler()->notifyUpdateFinished(); } } @@ -625,6 +669,28 @@ void DapEngine::handleThreadsResponse(const QJsonObject &response) } } +void DapEngine::handleEvaluateResponse(const QJsonObject &response) +{ + WatchItem *watchItem = watchHandler()->findItem( + m_variablesHandler->currentItem().iname); + if (watchItem + && response.value("body").toObject().contains("variablesReference")) { + watchItem->variablesReference + = response.value("body").toObject().value("variablesReference").toInt(); + watchItem->value + = response.value("body").toObject().value("result").toString(); + watchItem->type = response.value("body").toObject().value("type").toString(); + watchItem->wantsChildren = watchItem->variablesReference > 0; + + watchItem->updateValueCache(); + watchItem->update(); + + m_variablesHandler->addVariable(watchItem->iname, + watchItem->variablesReference); + } + m_variablesHandler->handleNext(); +} + void DapEngine::handleEvent(DapEventType type, const QJsonObject &event) { const QString eventType = event.value("event").toString(); @@ -740,51 +806,42 @@ void DapEngine::handleBreakpointEvent(const QJsonObject &event) void DapEngine::refreshLocals(const QJsonArray &variables) { + WatchItem *currentItem = watchHandler()->findItem(m_variablesHandler->currentItem().iname); + if (currentItem && currentItem->iname.startsWith("watch")) + currentItem->removeChildren(); + for (auto variable : variables) { WatchItem *item = new WatchItem; const QString name = variable.toObject().value("name").toString(); - if (m_isFirstLayer) - item->iname = "local." + name; - else - item->iname = m_currentWatchItem->iname + "." + name; + + item->iname = (currentItem ? currentItem->iname : "local") + "." + + name; item->name = name; item->type = variable.toObject().value("type").toString(); item->value = variable.toObject().value("value").toString(); item->address = variable.toObject().value("address").toInt(); item->type = variable.toObject().value("type").toString(); + item->variablesReference = variable.toObject().value("variablesReference").toInt(); + item->wantsChildren = item->variablesReference > 0; - const int variablesReference = variable.toObject().value("variablesReference").toInt(); - item->variablesReference = variablesReference; - if (variablesReference > 0) - item->wantsChildren = true; - - qCDebug(logCategory()) << "variable" << item->iname << variablesReference; - if (m_isFirstLayer) - m_watchItems.append(item); + qCDebug(logCategory()) << "variable" << item->iname << item->variablesReference; + if (currentItem) + currentItem->appendChild(item); else - m_currentWatchItem->appendChild(item); + watchHandler()->insertItem(item); } - if (m_isFirstLayer) { - if (m_variablesReferenceQueue.empty()) { - for (auto item : m_watchItems) - watchHandler()->insertItem(item); - m_isFirstLayer = false; - watchHandler()->notifyUpdateFinished(); - } else { - m_dapClient->variables(m_variablesReferenceQueue.front()); - m_variablesReferenceQueue.pop(); - } - return; + QModelIndex idx = watchHandler()->model()->indexForItem(currentItem); + if (currentItem && idx.isValid() && idx.data(LocalsExpandedRole).toBool()) { + emit watchHandler()->model()->inameIsExpanded(currentItem->iname); + emit watchHandler()->model()->itemIsExpanded(idx); } - if (m_currentWatchItem) { - emit watchHandler()->model()->inameIsExpanded(m_currentWatchItem->iname); - emit watchHandler()->model()->itemIsExpanded( - watchHandler()->model()->indexForItem(m_currentWatchItem)); + if (m_variablesHandler->queueSize() == 1 && currentItem == nullptr) { + watchHandler()->notifyUpdateFinished(); } - getVariableFromQueue(); + m_variablesHandler->handleNext(); } void DapEngine::refreshStack(const QJsonArray &stackFrames) diff --git a/src/plugins/debugger/dap/dapengine.h b/src/plugins/debugger/dap/dapengine.h index 5d197f94153..52b84a36a6f 100644 --- a/src/plugins/debugger/dap/dapengine.h +++ b/src/plugins/debugger/dap/dapengine.h @@ -20,6 +20,30 @@ class IDataProvider; class GdbMi; enum class DapResponseType; enum class DapEventType; +class DapEngine; + +class VariablesHandler { +public: + VariablesHandler(DapEngine *dapEngine); + + struct VariableItem { + QString iname; + int variablesReference; + }; + + void addVariable(const QString &iname, int variablesReference); + void handleNext(); + + VariableItem currentItem() const { return m_currentVarItem; } + int queueSize() const { return m_queue.size(); } + +private: + void startHandling(); + + DapEngine *m_dapEngine; + std::list m_queue; + VariableItem m_currentVarItem; +}; /* * A debugger engine for the debugger adapter protocol. @@ -27,9 +51,12 @@ enum class DapEventType; class DapEngine : public DebuggerEngine { public: - DapEngine() = default; + DapEngine(); ~DapEngine() override = default; + DapClient *dapClient() const { return m_dapClient; } + int currentStackFrameId() const { return m_currentStackFrameId; } + protected: void executeStepIn(bool) override; void executeStepOut() override; @@ -55,13 +82,10 @@ protected: void updateBreakpoint(const Breakpoint &bp) override; void removeBreakpoint(const Breakpoint &bp) override; - void assignValueInDebugger(WatchItem *item, - const QString &expr, const QVariant &value) override; void executeDebuggerCommand(const QString &command) override; void loadSymbols(const Utils::FilePath &moduleName) override; void loadAllSymbols() override; - void requestModuleSymbols(const Utils::FilePath &moduleName) override; void reloadModules() override; void reloadRegisters() override {} void reloadSourceFiles() override {} @@ -70,6 +94,7 @@ protected: bool supportsThreads() const { return true; } void updateItem(const QString &iname) override; void reexpandItems(const QSet &inames) override; + void doUpdateLocals(const UpdateParameters ¶ms) override; void getVariableFromQueue(); void runCommand(const DebuggerCommand &cmd) override; @@ -102,6 +127,7 @@ protected: void handleStackTraceResponse(const QJsonObject &response); void handleScopesResponse(const QJsonObject &response); void handleThreadsResponse(const QJsonObject &response); + void handleEvaluateResponse(const QJsonObject &response); void handleEvent(DapEventType type, const QJsonObject &event); void handleBreakpointEvent(const QJsonObject &event); @@ -118,11 +144,7 @@ protected: int m_currentThreadId = -1; int m_currentStackFrameId = -1; - bool m_isFirstLayer = true; - std::queue m_variablesReferenceQueue; - std::queue m_variablesReferenceInameQueue; - WatchItem *m_currentWatchItem = nullptr; - QList m_watchItems; + std::unique_ptr m_variablesHandler; virtual const QLoggingCategory &logCategory() {