From 8b1047aa8fd1ddf21032baa4fab19f98f4dc2b74 Mon Sep 17 00:00:00 2001 From: Artem Sokolovskii Date: Wed, 9 Aug 2023 14:09:02 +0200 Subject: [PATCH] DAP: Refactor dap responses handling Change-Id: Iebc16d7e497b4f3a1deec6f52f7b89815c2cde3b Reviewed-by: hjk --- src/plugins/debugger/dap/dapengine.cpp | 416 +++++++++++++----------- src/plugins/debugger/dap/dapengine.h | 12 +- src/plugins/debugger/debuggerplugin.cpp | 1 - 3 files changed, 231 insertions(+), 198 deletions(-) diff --git a/src/plugins/debugger/dap/dapengine.cpp b/src/plugins/debugger/dap/dapengine.cpp index db4ef9da69e..bafd90d5004 100644 --- a/src/plugins/debugger/dap/dapengine.cpp +++ b/src/plugins/debugger/dap/dapengine.cpp @@ -141,23 +141,10 @@ void DapEngine::shutdownEngine() m_dataGenerator->kill(); } -// From the docs: -// The sequence of events/requests is as follows: -// * adapters sends initialized event (after the initialize request has returned) -// * client sends zero or more setBreakpoints requests -// * client sends one setFunctionBreakpoints request -// (if corresponding capability supportsFunctionBreakpoints is true) -// * client sends a setExceptionBreakpoints request if one or more exceptionBreakpointFilters -// have been defined (or if supportsConfigurationDoneRequest is not true) -// * client sends other future configuration requests -// * client sends one configurationDone request to indicate the end of the configuration. - void DapEngine::handleDapStarted() { QTC_ASSERT(state() == EngineRunRequested, qCDebug(dapEngineLog) << state()); -// CHECK_STATE(EngineRunRequested); - postDirectCommand({ {"command", "initialize"}, {"type", "request"}, @@ -174,8 +161,6 @@ void DapEngine::handleDapConfigurationDone() { QTC_ASSERT(state() == EngineRunRequested, qCDebug(dapEngineLog) << state()); - // CHECK_STATE(EngineRunRequested); - postDirectCommand({ {"command", "configurationDone"}, {"type", "request"} @@ -402,7 +387,7 @@ void DapEngine::insertBreakpoint(const Breakpoint &bp) }} }); - notifyBreakpointChangeOk(bp); + notifyBreakpointInsertOk(bp); qCDebug(dapEngineLog) << "insertBreakpoint" << bp->modelId() << bp->responseId(); } @@ -662,197 +647,236 @@ void DapEngine::handleOutput(const QJsonDocument &data) const QJsonValue t = ob.value("type"); const QString type = t.toString(); - qCDebug(dapEngineLog) << "response" << ob; + + qCDebug(dapEngineLog) << "dap response" << ob; if (type == "response") { - const QString command = ob.value("command").toString(); - if (command == "configurationDone") { - showMessage("configurationDone", LogDebug); - qCDebug(dapEngineLog) << "configurationDone success"; - notifyEngineRunAndInferiorRunOk(); - return; - } - - if (command == "continue") { - showMessage("continue", LogDebug); - qCDebug(dapEngineLog) << "continue success"; - notifyInferiorRunOk(); - return; - } - - if (command == "stackTrace") { - QJsonArray stackFrames = ob.value("body").toObject().value("stackFrames").toArray(); - if (stackFrames.isEmpty()) - return; - - QJsonObject stackFrame = stackFrames[0].toObject(); - const FilePath file = FilePath::fromString( - stackFrame.value("source").toObject().value("path").toString()); - const int line = stackFrame.value("line").toInt(); - qCDebug(dapEngineLog) << "stackTrace success" << file << line; - gotoLocation(Location(file, line)); - - refreshStack(stackFrames); - dapScopes(stackFrame.value("id").toInt()); - return; - } - - if (command == "scopes") { - if (ob.value("success").toBool()) { - auto scopes = ob.value("body").toObject().value("scopes").toArray(); - for (auto scope : scopes) { - const QString name = scope.toObject().value("name").toString(); - const int variablesReference = scope.toObject().value("variablesReference").toInt(); - qCDebug(dapEngineLog) << "scoped success" << name << variablesReference; - if (name == "Locals") { // Fix for several scopes - m_rootWatchItem = new WatchItem(); - m_currentWatchItem = m_rootWatchItem; - watchHandler()->removeAllData(); - watchHandler()->notifyUpdateStarted(); - dapVariables(variablesReference); - } - } - } - } - - if (command == "variables") { - auto variables = ob.value("body").toObject().value("variables").toArray(); - refreshLocals(variables); - } - - if (command == "stepIn" || command == "stepOut" || command == "next") { - if (ob.value("success").toBool()) { - showMessage(command, LogDebug); - notifyInferiorRunOk(); - } else { - notifyInferiorRunFailed(); - } - return; - } - - if (command == "threads") { - QJsonArray threads = ob.value("body").toObject().value("threads").toArray(); - - if (threads.isEmpty()) - return; - - ThreadsHandler *handler = threadsHandler(); - for (auto thread : threads) { - ThreadData threadData; - threadData.id = QString::number(thread.toObject().value("id").toInt()); - threadData.name = thread.toObject().value("name").toString(); - handler->updateThread(threadData); - } - - if (m_currentThreadId) - handler->setCurrentThread( - threadsHandler()->threadForId(QString::number(m_currentThreadId))); - return; - } + handleResponse(ob); + return; } if (type == "event") { - const QString event = ob.value("event").toString(); - const QJsonObject body = ob.value("body").toObject(); - - if (event == "exited") { - notifyInferiorExited(); - showMessage("exited", LogDebug); - return; - } - - if (event == "output") { - const QString category = body.value("category").toString(); - const QString output = body.value("output").toString(); - if (category == "stdout") - showMessage(output, AppOutput); - else if (category == "stderr") - showMessage(output, AppError); - else - showMessage(output, LogDebug); - return; - } - qCDebug(dapEngineLog) << data; - - if (event == "initialized") { - showMessage(event, LogDebug); - qCDebug(dapEngineLog) << "initialize success"; - handleDapLaunch(); - handleDapConfigurationDone(); - return; - } - - if (event == "stopped") { - m_currentThreadId = body.value("threadId").toInt(); - showMessage(event, LogDebug); - if (body.value("reason").toString() == "breakpoint") { - QString id = QString::number( - body.value("hitBreakpointIds").toArray().first().toInteger()); - - Breakpoint bp = breakHandler()->findBreakpointByResponseId(id); - if (bp) { - const BreakpointParameters ¶ms = bp->requestedParameters(); - gotoLocation(Location(params.fileName, params.textPosition)); - } - } - - if (state() == InferiorStopRequested) - notifyInferiorStopOk(); - else - notifyInferiorSpontaneousStop(); - - dapStackTrace(); - threads(); - return; - } - - if (event == "thread") { - // threads(); // breaks cmake debugging for now - - showMessage(event, LogDebug); - if (body.value("reason").toString() == "started" && body.value("threadId").toInt() == 1) - claimInitialBreakpoints(); - return; - } - - if (event == "breakpoint") { - showMessage(event, LogDebug); - QJsonObject breakpoint = body.value("breakpoint").toObject(); - Breakpoint bp = breakHandler()->findBreakpointByResponseId( - QString::number(breakpoint.value("id").toInt())); - qCDebug(dapEngineLog) << "breakpoint id :" << breakpoint.value("id").toInt(); - - if (body.value("reason").toString() == "new") { - if (breakpoint.value("verified").toBool()) { - notifyBreakpointInsertOk(bp); - qCDebug(dapEngineLog) << "breakpoint inserted"; - } else { - notifyBreakpointInsertFailed(bp); - qCDebug(dapEngineLog) << "breakpoint insertion failed"; - } - return; - } - - if (body.value("reason").toString() == "removed") { - if (breakpoint.value("verified").toBool()) { - notifyBreakpointRemoveOk(bp); - qCDebug(dapEngineLog) << "breakpoint removed"; - } else { - notifyBreakpointRemoveFailed(bp); - qCDebug(dapEngineLog) << "breakpoint remove failed"; - } - return; - } - return; - } - - showMessage("UNKNOWN EVENT:" + event); + handleEvent(ob); return; } showMessage("UNKNOWN TYPE:" + type); } +void DapEngine::handleResponse(const QJsonObject &response) +{ + const QString command = response.value("command").toString(); + + if (command == "configurationDone") { + showMessage("configurationDone", LogDebug); + qCDebug(dapEngineLog) << "configurationDone success"; + notifyEngineRunAndInferiorRunOk(); + return; + } + + if (command == "continue") { + showMessage("continue", LogDebug); + qCDebug(dapEngineLog) << "continue success"; + notifyInferiorRunOk(); + return; + } + + if (command == "stackTrace") { + handleStackTraceResponse(response); + return; + } + + if (command == "scopes") { + handleScopesResponse(response); + return; + } + + if (command == "variables") { + auto variables = response.value("body").toObject().value("variables").toArray(); + refreshLocals(variables); + return; + } + + if (command == "stepIn" || command == "stepOut" || command == "next") { + if (response.value("success").toBool()) { + showMessage(command, LogDebug); + notifyInferiorRunOk(); + } else { + notifyInferiorRunFailed(); + } + return; + } + + if (command == "threads") { + handleThreadsResponse(response); + return; + } + + showMessage("UNKNOWN RESPONSE:" + command); +} + +void DapEngine::handleStackTraceResponse(const QJsonObject &response) +{ + QJsonArray stackFrames = response.value("body").toObject().value("stackFrames").toArray(); + if (stackFrames.isEmpty()) + return; + + QJsonObject stackFrame = stackFrames[0].toObject(); + const FilePath file = FilePath::fromString( + stackFrame.value("source").toObject().value("path").toString()); + const int line = stackFrame.value("line").toInt(); + qCDebug(dapEngineLog) << "stackTrace success" << file << line; + gotoLocation(Location(file, line)); + + refreshStack(stackFrames); + dapScopes(stackFrame.value("id").toInt()); +} + +void DapEngine::handleScopesResponse(const QJsonObject &response) +{ + if (!response.value("success").toBool()) + return; + + QJsonArray scopes = response.value("body").toObject().value("scopes").toArray(); + for (const QJsonValueRef &scope : scopes) { + const QString name = scope.toObject().value("name").toString(); + const int variablesReference = scope.toObject().value("variablesReference").toInt(); + qCDebug(dapEngineLog) << "scoped success" << name << variablesReference; + if (name == "Locals") { // Fix for several scopes + m_rootWatchItem = new WatchItem(); + m_currentWatchItem = m_rootWatchItem; + watchHandler()->removeAllData(); + watchHandler()->notifyUpdateStarted(); + dapVariables(variablesReference); + } + } +} + +void DapEngine::handleThreadsResponse(const QJsonObject &response) +{ + QJsonArray threads = response.value("body").toObject().value("threads").toArray(); + + if (threads.isEmpty()) + return; + + ThreadsHandler *handler = threadsHandler(); + for (const QJsonValueRef &thread : threads) { + ThreadData threadData; + threadData.id = QString::number(thread.toObject().value("id").toInt()); + threadData.name = thread.toObject().value("name").toString(); + handler->updateThread(threadData); + } + + if (m_currentThreadId) + handler->setCurrentThread(threadsHandler()->threadForId(QString::number(m_currentThreadId))); +} + +void DapEngine::handleEvent(const QJsonObject &event) +{ + const QString eventType = event.value("event").toString(); + const QJsonObject body = event.value("body").toObject(); + showMessage(eventType, LogDebug); + + if (eventType == "exited") { + notifyInferiorExited(); + return; + } + + if (eventType == "output") { + const QString category = body.value("category").toString(); + const QString output = body.value("output").toString(); + if (category == "stdout") + showMessage(output, AppOutput); + else if (category == "stderr") + showMessage(output, AppError); + else + showMessage(output, LogDebug); + return; + } + + if (eventType == "initialized") { + qCDebug(dapEngineLog) << "initialize success"; + handleDapLaunch(); + handleDapConfigurationDone(); + return; + } + + if (eventType == "stopped") { + handleStoppedEvent(event); + return; + } + + if (eventType == "thread") { + threads(); + if (body.value("reason").toString() == "started" && body.value("threadId").toInt() == 1) + claimInitialBreakpoints(); + return; + } + + if (eventType == "breakpoint") { + handleBreakpointEvent(event); + return; + } + + showMessage("UNKNOWN EVENT:" + eventType); +} + +void DapEngine::handleStoppedEvent(const QJsonObject &event) +{ + const QJsonObject body = event.value("body").toObject(); + m_currentThreadId = body.value("threadId").toInt(); + + if (body.value("reason").toString() == "breakpoint") { + QString id = QString::number(body.value("hitBreakpointIds").toArray().first().toInteger()); + + Breakpoint bp = breakHandler()->findBreakpointByResponseId(id); + if (bp) { + const BreakpointParameters ¶ms = bp->requestedParameters(); + gotoLocation(Location(params.fileName, params.textPosition)); + } + } + + if (state() == InferiorStopRequested) + notifyInferiorStopOk(); + else + notifyInferiorSpontaneousStop(); + + dapStackTrace(); + threads(); +} + +void DapEngine::handleBreakpointEvent(const QJsonObject &event) +{ + const QJsonObject body = event.value("body").toObject(); + QJsonObject breakpoint = body.value("breakpoint").toObject(); + + Breakpoint bp = breakHandler()->findBreakpointByResponseId( + QString::number(breakpoint.value("id").toInt())); + qCDebug(dapEngineLog) << "breakpoint id :" << breakpoint.value("id").toInt(); + + if (body.value("reason").toString() == "new") { + if (breakpoint.value("verified").toBool()) { + notifyBreakpointInsertOk(bp); + qCDebug(dapEngineLog) << "breakpoint inserted"; + } else { + notifyBreakpointInsertFailed(bp); + qCDebug(dapEngineLog) << "breakpoint insertion failed"; + } + return; + } + + if (body.value("reason").toString() == "removed") { + if (breakpoint.value("verified").toBool()) { + notifyBreakpointRemoveOk(bp); + qCDebug(dapEngineLog) << "breakpoint removed"; + } else { + notifyBreakpointRemoveFailed(bp); + qCDebug(dapEngineLog) << "breakpoint remove failed"; + } + return; + } +} + void DapEngine::refreshLocals(const QJsonArray &variables) { for (auto variable : variables) { diff --git a/src/plugins/debugger/dap/dapengine.h b/src/plugins/debugger/dap/dapengine.h index 1950713f15d..acc36d1182c 100644 --- a/src/plugins/debugger/dap/dapengine.h +++ b/src/plugins/debugger/dap/dapengine.h @@ -117,8 +117,18 @@ protected: void handleDapDone(); void readDapStandardOutput(); void readDapStandardError(); + void handleOutput(const QJsonDocument &data); - void handleResponse(const QString &ba); + + void handleResponse(const QJsonObject &response); + void handleStackTraceResponse(const QJsonObject &response); + void handleScopesResponse(const QJsonObject &response); + void handleThreadsResponse(const QJsonObject &response); + + void handleEvent(const QJsonObject &event); + void handleBreakpointEvent(const QJsonObject &event); + void handleStoppedEvent(const QJsonObject &event); + void updateAll() override; void updateLocals() override; void connectDataGeneratorSignals(); diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 7d39b9ed725..6e557c4c2b2 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -1179,7 +1179,6 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments) m_startCmakeAction.setText(Tr::tr("Start CMake Debugging")); m_startCmakeAction.setEnabled(true); m_startCmakeAction.setIcon(startIcon(true)); - m_startCmakeAction.setToolButtonStyle(Qt::ToolButtonTextBesideIcon); m_startCmakeAction.setVisible(true); m_perspectiveCmake->addToolBarAction(&m_startCmakeAction);