2023-01-03 15:12:43 +01:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
|
|
|
|
|
|
#include "dapengine.h"
|
|
|
|
|
|
|
|
#include <debugger/breakhandler.h>
|
|
|
|
#include <debugger/debuggeractions.h>
|
|
|
|
#include <debugger/debuggercore.h>
|
|
|
|
#include <debugger/debuggerdialogs.h>
|
2023-07-04 16:32:37 +02:00
|
|
|
#include <debugger/debuggerinternalconstants.h>
|
|
|
|
#include <debugger/debuggermainwindow.h>
|
2023-01-03 15:12:43 +01:00
|
|
|
#include <debugger/debuggerplugin.h>
|
|
|
|
#include <debugger/debuggerprotocol.h>
|
2023-07-04 16:32:37 +02:00
|
|
|
#include <debugger/debuggerruncontrol.h>
|
2023-01-03 15:12:43 +01:00
|
|
|
#include <debugger/debuggertooltipmanager.h>
|
|
|
|
#include <debugger/debuggertr.h>
|
|
|
|
#include <debugger/moduleshandler.h>
|
|
|
|
#include <debugger/procinterrupt.h>
|
|
|
|
#include <debugger/registerhandler.h>
|
|
|
|
#include <debugger/sourceutils.h>
|
|
|
|
#include <debugger/stackhandler.h>
|
|
|
|
#include <debugger/threaddata.h>
|
|
|
|
#include <debugger/watchhandler.h>
|
|
|
|
#include <debugger/watchutils.h>
|
|
|
|
|
2023-07-04 16:32:37 +02:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
#include <utils/algorithm.h>
|
|
|
|
#include <utils/environment.h>
|
2023-05-03 17:05:35 +02:00
|
|
|
#include <utils/process.h>
|
2023-01-03 15:12:43 +01:00
|
|
|
#include <utils/qtcassert.h>
|
2023-07-27 16:26:15 +02:00
|
|
|
#include <utils/temporarydirectory.h>
|
2023-01-03 15:12:43 +01:00
|
|
|
|
2023-06-13 17:24:28 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
|
|
|
#include <coreplugin/editormanager/ieditor.h>
|
2023-01-03 15:12:43 +01:00
|
|
|
#include <coreplugin/icore.h>
|
2023-06-13 17:24:28 +02:00
|
|
|
#include <coreplugin/idocument.h>
|
2023-01-03 15:12:43 +01:00
|
|
|
#include <coreplugin/messagebox.h>
|
|
|
|
|
2023-07-04 16:32:37 +02:00
|
|
|
#include <projectexplorer/buildconfiguration.h>
|
|
|
|
#include <projectexplorer/buildsystem.h>
|
|
|
|
#include <projectexplorer/projecttree.h>
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
#include <QDateTime>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QFileInfo>
|
|
|
|
#include <QJsonArray>
|
|
|
|
#include <QJsonDocument>
|
2023-06-27 16:57:51 +02:00
|
|
|
#include <QLocalSocket>
|
2023-01-03 15:12:43 +01:00
|
|
|
#include <QThread>
|
2023-06-27 16:57:51 +02:00
|
|
|
#include <QTimer>
|
|
|
|
#include <QVariant>
|
2023-01-03 15:12:43 +01:00
|
|
|
|
|
|
|
using namespace Core;
|
|
|
|
using namespace Utils;
|
|
|
|
|
2023-07-04 16:32:37 +02:00
|
|
|
static Q_LOGGING_CATEGORY(dapEngineLog, "qtc.dbg.dapengine", QtWarningMsg)
|
2023-06-27 16:57:51 +02:00
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
namespace Debugger::Internal {
|
|
|
|
|
2023-06-27 16:57:51 +02:00
|
|
|
class ProcessDataProvider : public IDataProvider
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ProcessDataProvider(const DebuggerRunParameters &rp, const CommandLine &cmd)
|
|
|
|
: m_runParameters(rp)
|
|
|
|
, m_cmd(cmd)
|
|
|
|
{
|
|
|
|
connect(&m_proc, &Process::started, this, &IDataProvider::started);
|
|
|
|
connect(&m_proc, &Process::done, this, &IDataProvider::done);
|
|
|
|
connect(&m_proc,
|
|
|
|
&Process::readyReadStandardOutput,
|
|
|
|
this,
|
|
|
|
&IDataProvider::readyReadStandardOutput);
|
|
|
|
connect(&m_proc,
|
|
|
|
&Process::readyReadStandardError,
|
|
|
|
this,
|
|
|
|
&IDataProvider::readyReadStandardError);
|
|
|
|
}
|
|
|
|
|
|
|
|
~ProcessDataProvider()
|
|
|
|
{
|
|
|
|
m_proc.kill();
|
|
|
|
m_proc.waitForFinished();
|
|
|
|
}
|
|
|
|
|
|
|
|
void start() override
|
|
|
|
{
|
|
|
|
m_proc.setProcessMode(ProcessMode::Writer);
|
|
|
|
m_proc.setEnvironment(m_runParameters.debugger.environment);
|
|
|
|
m_proc.setCommand(m_cmd);
|
|
|
|
m_proc.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isRunning() const override { return m_proc.isRunning(); }
|
|
|
|
void writeRaw(const QByteArray &data) override { m_proc.writeRaw(data); }
|
|
|
|
void kill() override { m_proc.kill(); }
|
|
|
|
QByteArray readAllStandardOutput() override { return m_proc.readAllStandardOutput().toUtf8(); }
|
|
|
|
QString readAllStandardError() override { return m_proc.readAllStandardError(); }
|
|
|
|
int exitCode() const override { return m_proc.exitCode(); }
|
|
|
|
QString executable() const override { return m_proc.commandLine().executable().toUserOutput(); }
|
|
|
|
|
|
|
|
QProcess::ExitStatus exitStatus() const override { return m_proc.exitStatus(); }
|
|
|
|
QProcess::ProcessError error() const override { return m_proc.error(); }
|
|
|
|
Utils::ProcessResult result() const override { return m_proc.result(); }
|
|
|
|
QString exitMessage() const override { return m_proc.exitMessage(); };
|
|
|
|
|
|
|
|
private:
|
|
|
|
Utils::Process m_proc;
|
|
|
|
const DebuggerRunParameters m_runParameters;
|
|
|
|
const CommandLine m_cmd;
|
|
|
|
};
|
|
|
|
|
|
|
|
class LocalSocketDataProvider : public IDataProvider
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
LocalSocketDataProvider(const QString &socketName)
|
|
|
|
: m_socketName(socketName)
|
|
|
|
{
|
|
|
|
connect(&m_socket, &QLocalSocket::connected, this, &IDataProvider::started);
|
|
|
|
connect(&m_socket, &QLocalSocket::disconnected, this, &IDataProvider::done);
|
|
|
|
connect(&m_socket, &QLocalSocket::readyRead, this, &IDataProvider::readyReadStandardOutput);
|
|
|
|
connect(&m_socket,
|
|
|
|
&QLocalSocket::errorOccurred,
|
|
|
|
this,
|
|
|
|
&IDataProvider::readyReadStandardError);
|
|
|
|
}
|
|
|
|
|
|
|
|
~LocalSocketDataProvider() { m_socket.disconnectFromServer(); }
|
|
|
|
|
|
|
|
void start() override { m_socket.connectToServer(m_socketName, QIODevice::ReadWrite); }
|
|
|
|
|
|
|
|
bool isRunning() const override { return m_socket.isOpen(); }
|
|
|
|
void writeRaw(const QByteArray &data) override { m_socket.write(data); }
|
2023-08-03 13:23:03 +02:00
|
|
|
void kill() override {
|
|
|
|
if (m_socket.isOpen())
|
|
|
|
m_socket.disconnectFromServer();
|
|
|
|
else {
|
|
|
|
m_socket.abort();
|
|
|
|
emit done();
|
|
|
|
}
|
|
|
|
}
|
2023-06-27 16:57:51 +02:00
|
|
|
QByteArray readAllStandardOutput() override { return m_socket.readAll(); }
|
|
|
|
QString readAllStandardError() override { return QString(); }
|
|
|
|
int exitCode() const override { return 0; }
|
|
|
|
QString executable() const override { return m_socket.serverName(); }
|
|
|
|
|
|
|
|
QProcess::ExitStatus exitStatus() const override { return QProcess::NormalExit; }
|
|
|
|
QProcess::ProcessError error() const override { return QProcess::UnknownError; }
|
|
|
|
Utils::ProcessResult result() const override { return ProcessResult::FinishedWithSuccess; }
|
|
|
|
QString exitMessage() const override { return QString(); };
|
|
|
|
|
|
|
|
private:
|
|
|
|
QLocalSocket m_socket;
|
|
|
|
const QString m_socketName;
|
|
|
|
};
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
DapEngine::DapEngine()
|
|
|
|
{
|
|
|
|
setObjectName("DapEngine");
|
|
|
|
setDebuggerName("DAP");
|
2023-07-13 13:43:59 +02:00
|
|
|
m_rootWatchItem = new WatchItem();
|
|
|
|
m_currentWatchItem = m_rootWatchItem;
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
2023-04-06 14:20:01 +02:00
|
|
|
void DapEngine::executeDebuggerCommand(const QString &/*command*/)
|
2023-01-03 15:12:43 +01:00
|
|
|
{
|
2023-06-27 16:57:51 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qCDebug(dapEngineLog) << state());
|
2023-01-03 15:12:43 +01:00
|
|
|
// if (state() == DebuggerNotReady) {
|
|
|
|
// showMessage("DAP PROCESS NOT RUNNING, PLAIN CMD IGNORED: " + command);
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
// QTC_ASSERT(m_proc.isRunning(), notifyEngineIll());
|
|
|
|
// postDirectCommand(command);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::postDirectCommand(const QJsonObject &ob)
|
|
|
|
{
|
|
|
|
static int seq = 1;
|
|
|
|
QJsonObject obseq = ob;
|
|
|
|
obseq.insert("seq", seq++);
|
|
|
|
|
|
|
|
const QByteArray data = QJsonDocument(obseq).toJson(QJsonDocument::Compact);
|
|
|
|
const QByteArray msg = "Content-Length: " + QByteArray::number(data.size()) + "\r\n\r\n" + data;
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << msg;
|
2023-01-03 15:12:43 +01:00
|
|
|
|
2023-06-27 16:57:51 +02:00
|
|
|
m_dataGenerator->writeRaw(msg);
|
2023-01-03 15:12:43 +01:00
|
|
|
|
|
|
|
showMessage(QString::fromUtf8(msg), LogInput);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::runCommand(const DebuggerCommand &cmd)
|
|
|
|
{
|
|
|
|
if (state() == EngineSetupRequested) { // cmd has been triggered too early
|
|
|
|
showMessage("IGNORED COMMAND: " + cmd.function);
|
|
|
|
return;
|
|
|
|
}
|
2023-06-27 16:57:51 +02:00
|
|
|
QTC_ASSERT(m_dataGenerator->isRunning(), notifyEngineIll());
|
2023-01-03 15:12:43 +01:00
|
|
|
// postDirectCommand(cmd.args.toObject());
|
|
|
|
// const QByteArray data = QJsonDocument(cmd.args.toObject()).toJson(QJsonDocument::Compact);
|
|
|
|
// m_proc.writeRaw("Content-Length: " + QByteArray::number(data.size()) + "\r\n" + data + "\r\n");
|
|
|
|
|
|
|
|
// showMessage(QString::fromUtf8(data), LogInput);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::shutdownInferior()
|
|
|
|
{
|
2023-06-27 16:57:51 +02:00
|
|
|
QTC_ASSERT(state() == InferiorShutdownRequested, qCDebug(dapEngineLog) << state());
|
|
|
|
|
|
|
|
postDirectCommand({
|
|
|
|
{"command", "disconnect"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
|
|
|
{"restart", false},
|
|
|
|
{"terminateDebuggee", true}
|
|
|
|
}}
|
|
|
|
});
|
2023-01-03 15:12:43 +01:00
|
|
|
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "DapEngine::shutdownInferior()";
|
2023-01-03 15:12:43 +01:00
|
|
|
notifyInferiorShutdownFinished();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::shutdownEngine()
|
|
|
|
{
|
2023-06-27 16:57:51 +02:00
|
|
|
QTC_ASSERT(state() == EngineShutdownRequested, qCDebug(dapEngineLog) << state());
|
2023-01-03 15:12:43 +01:00
|
|
|
|
2023-06-27 16:57:51 +02:00
|
|
|
postDirectCommand({
|
|
|
|
{"command", "terminate"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
|
|
|
{"restart", false}
|
|
|
|
}}
|
|
|
|
});
|
|
|
|
|
|
|
|
qCDebug(dapEngineLog) << "DapEngine::shutdownEngine()";
|
|
|
|
m_dataGenerator->kill();
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::setupEngine()
|
|
|
|
{
|
2023-06-27 16:57:51 +02:00
|
|
|
QTC_ASSERT(state() == EngineSetupRequested, qCDebug(dapEngineLog) << state());
|
|
|
|
|
2023-07-04 16:32:37 +02:00
|
|
|
const auto connectDataGeneratorSignals = [this] {
|
|
|
|
if (!m_dataGenerator)
|
|
|
|
return;
|
|
|
|
|
|
|
|
connect(m_dataGenerator.get(), &IDataProvider::started, this, &DapEngine::handleDapStarted);
|
|
|
|
connect(m_dataGenerator.get(), &IDataProvider::done, this, &DapEngine::handleDapDone);
|
|
|
|
connect(m_dataGenerator.get(),
|
|
|
|
&IDataProvider::readyReadStandardOutput,
|
|
|
|
this,
|
|
|
|
&DapEngine::readDapStandardOutput);
|
|
|
|
connect(m_dataGenerator.get(),
|
|
|
|
&IDataProvider::readyReadStandardError,
|
|
|
|
this,
|
|
|
|
&DapEngine::readDapStandardError);
|
|
|
|
};
|
|
|
|
|
|
|
|
Perspective *currentPerspective = DebuggerMainWindow::instance()->currentPerspective();
|
|
|
|
if (currentPerspective->parentPerspectiveId() == Constants::CMAKE_PERSPECTIVE_ID) {
|
|
|
|
qCDebug(dapEngineLog) << "build system name" << ProjectExplorer::ProjectTree::currentBuildSystem()->name();
|
|
|
|
|
2023-07-27 16:26:15 +02:00
|
|
|
m_dataGenerator = std::make_unique<LocalSocketDataProvider>(
|
|
|
|
TemporaryDirectory::masterDirectoryPath() + "/cmake-dap.sock");
|
2023-07-04 16:32:37 +02:00
|
|
|
connectDataGeneratorSignals();
|
|
|
|
|
|
|
|
connect(ProjectExplorer::ProjectTree::currentBuildSystem(),
|
|
|
|
&ProjectExplorer::BuildSystem::debuggingStarted,
|
|
|
|
this,
|
|
|
|
[this] {
|
|
|
|
m_dataGenerator->start();
|
|
|
|
});
|
|
|
|
|
|
|
|
ProjectExplorer::ProjectTree::currentBuildSystem()->requestDebugging();
|
2023-06-27 16:57:51 +02:00
|
|
|
} else {
|
|
|
|
const DebuggerRunParameters &rp = runParameters();
|
|
|
|
const CommandLine cmd{rp.debugger.command.executable(), {"-i", "dap"}};
|
2023-01-03 15:12:43 +01:00
|
|
|
|
2023-07-04 16:32:37 +02:00
|
|
|
m_dataGenerator = std::make_unique<ProcessDataProvider>(rp, cmd);
|
|
|
|
connectDataGeneratorSignals();
|
|
|
|
m_dataGenerator->start();
|
2023-01-03 15:12:43 +01:00
|
|
|
|
2023-07-04 16:32:37 +02:00
|
|
|
}
|
2023-06-16 15:28:50 +02:00
|
|
|
notifyEngineSetupOk();
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
2023-06-16 15:28:50 +02:00
|
|
|
void DapEngine::handleDapStarted()
|
2023-01-03 15:12:43 +01:00
|
|
|
{
|
2023-06-27 16:57:51 +02:00
|
|
|
QTC_ASSERT(state() == EngineRunRequested, qCDebug(dapEngineLog) << state());
|
2023-01-03 15:12:43 +01:00
|
|
|
|
|
|
|
// CHECK_STATE(EngineRunRequested);
|
|
|
|
|
|
|
|
postDirectCommand({
|
|
|
|
{"command", "initialize"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject {
|
|
|
|
{"clientID", "QtCreator"}, // The ID of the client using this adapter.
|
2023-06-27 16:57:51 +02:00
|
|
|
{"clientName", "QtCreator"}, // The human-readable name of the client using this adapter.
|
|
|
|
{"adapterID", "cmake"},
|
|
|
|
{"pathFormat", "path"}
|
2023-01-03 15:12:43 +01:00
|
|
|
}}
|
|
|
|
});
|
|
|
|
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "handleDapStarted";
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
2023-06-16 15:28:50 +02:00
|
|
|
void DapEngine::handleDapConfigurationDone()
|
2023-01-03 15:12:43 +01:00
|
|
|
{
|
2023-06-27 16:57:51 +02:00
|
|
|
QTC_ASSERT(state() == EngineRunRequested, qCDebug(dapEngineLog) << state());
|
2023-01-03 15:12:43 +01:00
|
|
|
|
|
|
|
// CHECK_STATE(EngineRunRequested);
|
|
|
|
|
2023-06-27 16:57:51 +02:00
|
|
|
postDirectCommand({
|
|
|
|
{"command", "configurationDone"},
|
|
|
|
{"type", "request"}
|
|
|
|
});
|
2023-01-03 15:12:43 +01:00
|
|
|
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "handleDapConfigurationDone";
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-16 15:28:50 +02:00
|
|
|
void DapEngine::handleDapLaunch()
|
2023-01-03 15:12:43 +01:00
|
|
|
{
|
2023-06-27 16:57:51 +02:00
|
|
|
QTC_ASSERT(state() == EngineRunRequested, qCDebug(dapEngineLog) << state());
|
2023-01-03 15:12:43 +01:00
|
|
|
|
2023-06-13 17:24:28 +02:00
|
|
|
postDirectCommand({
|
|
|
|
{"command", "launch"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
|
|
|
{"noDebug", false},
|
|
|
|
{"program", runParameters().inferior.command.executable().path()},
|
|
|
|
{"__restart", ""}
|
|
|
|
}}
|
|
|
|
});
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "handleDapLaunch";
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::interruptInferior()
|
|
|
|
{
|
2023-06-13 17:24:28 +02:00
|
|
|
postDirectCommand({
|
|
|
|
{"command", "pause"},
|
|
|
|
{"type", "request"}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-06-16 15:28:50 +02:00
|
|
|
void DapEngine::dapStackTrace()
|
2023-06-13 17:24:28 +02:00
|
|
|
{
|
|
|
|
if (m_currentThreadId == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
postDirectCommand({
|
|
|
|
{"command", "stackTrace"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
|
|
|
{"threadId", m_currentThreadId},
|
|
|
|
{"startFrame", 0},
|
2023-06-16 15:28:50 +02:00
|
|
|
{"levels", 10}
|
|
|
|
}}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-07-04 16:32:37 +02:00
|
|
|
void DapEngine::dapScopes(int frameId)
|
2023-06-16 15:28:50 +02:00
|
|
|
{
|
|
|
|
postDirectCommand({
|
|
|
|
{"command", "scopes"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
2023-07-04 16:32:37 +02:00
|
|
|
{"frameId", frameId}
|
2023-06-13 17:24:28 +02:00
|
|
|
}}
|
|
|
|
});
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
2023-06-15 15:27:33 +02:00
|
|
|
void DapEngine::threads()
|
|
|
|
{
|
2023-06-16 15:28:50 +02:00
|
|
|
postDirectCommand({
|
|
|
|
{"command", "threads"},
|
|
|
|
{"type", "request"}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::dapVariables(int variablesReference)
|
|
|
|
{
|
|
|
|
postDirectCommand({
|
|
|
|
{"command", "variables"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
|
|
|
{"variablesReference", variablesReference}
|
|
|
|
}}
|
|
|
|
});
|
2023-06-15 15:27:33 +02:00
|
|
|
}
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
void DapEngine::executeStepIn(bool)
|
|
|
|
{
|
2023-06-13 17:24:28 +02:00
|
|
|
if (m_currentThreadId == -1)
|
|
|
|
return;
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
notifyInferiorRunRequested();
|
2023-04-06 14:20:01 +02:00
|
|
|
|
2023-06-13 17:24:28 +02:00
|
|
|
postDirectCommand({
|
|
|
|
{"command", "stepIn"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
|
|
|
{"threadId", m_currentThreadId},
|
|
|
|
}}
|
|
|
|
});
|
2023-04-06 14:20:01 +02:00
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::executeStepOut()
|
|
|
|
{
|
2023-06-13 17:24:28 +02:00
|
|
|
if (m_currentThreadId == -1)
|
|
|
|
return;
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
notifyInferiorRunRequested();
|
2023-06-13 17:24:28 +02:00
|
|
|
|
|
|
|
postDirectCommand({
|
|
|
|
{"command", "stepOut"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
|
|
|
{"threadId", m_currentThreadId},
|
|
|
|
}}
|
|
|
|
});
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::executeStepOver(bool)
|
|
|
|
{
|
2023-06-13 17:24:28 +02:00
|
|
|
if (m_currentThreadId == -1)
|
|
|
|
return;
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
notifyInferiorRunRequested();
|
2023-06-13 17:24:28 +02:00
|
|
|
|
|
|
|
postDirectCommand({
|
|
|
|
{"command", "next"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
|
|
|
{"threadId", m_currentThreadId},
|
|
|
|
}}
|
|
|
|
});
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::continueInferior()
|
|
|
|
{
|
2023-04-06 14:20:01 +02:00
|
|
|
notifyInferiorRunRequested();
|
2023-06-13 17:24:28 +02:00
|
|
|
postDirectCommand({
|
|
|
|
{"command", "continue"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
|
|
|
{"threadId", m_currentThreadId},
|
|
|
|
}}
|
|
|
|
});
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::executeRunToLine(const ContextData &data)
|
|
|
|
{
|
|
|
|
Q_UNUSED(data)
|
|
|
|
QTC_CHECK("FIXME: DapEngine::runToLineExec()" && false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::executeRunToFunction(const QString &functionName)
|
|
|
|
{
|
|
|
|
Q_UNUSED(functionName)
|
|
|
|
QTC_CHECK("FIXME: DapEngine::runToFunctionExec()" && false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::executeJumpToLine(const ContextData &data)
|
|
|
|
{
|
|
|
|
Q_UNUSED(data)
|
|
|
|
QTC_CHECK("FIXME: DapEngine::jumpToLineExec()" && false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::activateFrame(int frameIndex)
|
|
|
|
{
|
|
|
|
if (state() != InferiorStopOk && state() != InferiorUnrunnable)
|
|
|
|
return;
|
|
|
|
|
|
|
|
StackHandler *handler = stackHandler();
|
|
|
|
QTC_ASSERT(frameIndex < handler->stackSize(), return);
|
|
|
|
handler->setCurrentIndex(frameIndex);
|
|
|
|
gotoLocation(handler->currentFrame());
|
|
|
|
updateLocals();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::selectThread(const Thread &thread)
|
|
|
|
{
|
|
|
|
Q_UNUSED(thread)
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DapEngine::acceptsBreakpoint(const BreakpointParameters &) const
|
|
|
|
{
|
|
|
|
return true; // FIXME: Too bold.
|
|
|
|
}
|
|
|
|
|
2023-04-06 14:20:01 +02:00
|
|
|
static QJsonObject createBreakpoint(const Breakpoint &breakpoint)
|
|
|
|
{
|
|
|
|
const BreakpointParameters ¶ms = breakpoint->requestedParameters();
|
|
|
|
|
|
|
|
if (params.fileName.isEmpty())
|
|
|
|
return QJsonObject();
|
|
|
|
|
|
|
|
QJsonObject bp;
|
2023-06-06 15:44:59 +02:00
|
|
|
bp["line"] = params.textPosition.line;
|
2023-04-06 14:20:01 +02:00
|
|
|
bp["source"] = QJsonObject{{"name", params.fileName.fileName()},
|
|
|
|
{"path", params.fileName.path()}};
|
|
|
|
return bp;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
void DapEngine::insertBreakpoint(const Breakpoint &bp)
|
|
|
|
{
|
|
|
|
QTC_ASSERT(bp, return);
|
|
|
|
QTC_CHECK(bp->state() == BreakpointInsertionRequested);
|
|
|
|
notifyBreakpointInsertProceeding(bp);
|
|
|
|
|
|
|
|
const BreakpointParameters ¶ms = bp->requestedParameters();
|
2023-04-06 14:20:01 +02:00
|
|
|
bp->setResponseId(QString::number(m_nextBreakpointId++));
|
|
|
|
|
|
|
|
QJsonArray breakpoints;
|
|
|
|
for (const auto &breakpoint : breakHandler()->breakpoints()) {
|
|
|
|
QJsonObject jsonBp = createBreakpoint(breakpoint);
|
|
|
|
if (!jsonBp.isEmpty()
|
|
|
|
&& params.fileName.path() == jsonBp["source"].toObject()["path"].toString()) {
|
|
|
|
breakpoints.append(jsonBp);
|
|
|
|
}
|
|
|
|
}
|
2023-01-03 15:12:43 +01:00
|
|
|
|
2023-06-13 17:24:28 +02:00
|
|
|
postDirectCommand( {
|
|
|
|
{"command", "setBreakpoints"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
|
|
|
{"source", QJsonObject{
|
|
|
|
{"path", params.fileName.path()}
|
|
|
|
}},
|
|
|
|
{"breakpoints", breakpoints}
|
|
|
|
}}
|
|
|
|
});
|
2023-04-06 14:20:01 +02:00
|
|
|
|
2023-08-03 13:23:03 +02:00
|
|
|
notifyBreakpointChangeOk(bp);
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "insertBreakpoint" << bp->modelId() << bp->responseId();
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::updateBreakpoint(const Breakpoint &bp)
|
|
|
|
{
|
2023-04-06 14:20:01 +02:00
|
|
|
notifyBreakpointChangeProceeding(bp);
|
2023-01-03 15:12:43 +01:00
|
|
|
// QTC_ASSERT(bp, return);
|
|
|
|
// const BreakpointState state = bp->state();
|
|
|
|
// if (QTC_GUARD(state == BreakpointUpdateRequested))
|
|
|
|
// if (bp->responseId().isEmpty()) // FIXME postpone update somehow (QTimer::singleShot?)
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// // FIXME figure out what needs to be changed (there might be more than enabled state)
|
|
|
|
// const BreakpointParameters &requested = bp->requestedParameters();
|
|
|
|
// if (requested.enabled != bp->isEnabled()) {
|
|
|
|
// if (bp->isEnabled())
|
|
|
|
// postDirectCommand("disable " + bp->responseId());
|
|
|
|
// else
|
|
|
|
// postDirectCommand("enable " + bp->responseId());
|
|
|
|
// bp->setEnabled(!bp->isEnabled());
|
|
|
|
// }
|
|
|
|
// // Pretend it succeeds without waiting for response.
|
|
|
|
notifyBreakpointChangeOk(bp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::removeBreakpoint(const Breakpoint &bp)
|
|
|
|
{
|
|
|
|
QTC_ASSERT(bp, return);
|
|
|
|
QTC_CHECK(bp->state() == BreakpointRemoveRequested);
|
2023-04-06 14:20:01 +02:00
|
|
|
notifyBreakpointRemoveProceeding(bp);
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
const BreakpointParameters ¶ms = bp->requestedParameters();
|
2023-04-06 14:20:01 +02:00
|
|
|
|
|
|
|
QJsonArray breakpoints;
|
|
|
|
for (const auto &breakpoint : breakHandler()->breakpoints())
|
|
|
|
if (breakpoint->responseId() != bp->responseId()
|
|
|
|
&& params.fileName == breakpoint->requestedParameters().fileName) {
|
|
|
|
QJsonObject jsonBp = createBreakpoint(breakpoint);
|
|
|
|
breakpoints.append(jsonBp);
|
|
|
|
}
|
|
|
|
|
2023-06-13 17:24:28 +02:00
|
|
|
postDirectCommand({
|
|
|
|
{"command", "setBreakpoints"},
|
|
|
|
{"type", "request"},
|
|
|
|
{"arguments", QJsonObject{
|
|
|
|
{"source", QJsonObject{
|
|
|
|
{"path", params.fileName.path()}
|
|
|
|
}},
|
|
|
|
{"breakpoints", breakpoints}
|
|
|
|
}}
|
|
|
|
});
|
2023-01-03 15:12:43 +01:00
|
|
|
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "removeBreakpoint" << bp->modelId() << bp->responseId();
|
2023-08-03 13:23:03 +02:00
|
|
|
notifyBreakpointRemoveOk(bp);
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::loadSymbols(const Utils::FilePath &/*moduleName*/)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::loadAllSymbols()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::reloadModules()
|
|
|
|
{
|
|
|
|
runCommand({"listModules"});
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::refreshModules(const GdbMi &modules)
|
|
|
|
{
|
|
|
|
ModulesHandler *handler = modulesHandler();
|
|
|
|
handler->beginUpdateAll();
|
|
|
|
for (const GdbMi &item : modules) {
|
|
|
|
Module module;
|
|
|
|
module.moduleName = item["name"].data();
|
|
|
|
QString path = item["value"].data();
|
|
|
|
int pos = path.indexOf("' from '");
|
|
|
|
if (pos != -1) {
|
|
|
|
path = path.mid(pos + 8);
|
|
|
|
if (path.size() >= 2)
|
|
|
|
path.chop(2);
|
|
|
|
} else if (path.startsWith("<module '")
|
2023-06-27 16:57:51 +02:00
|
|
|
&& path.endsWith("' (built-in)>")) {
|
2023-01-03 15:12:43 +01:00
|
|
|
path = "(builtin)";
|
|
|
|
}
|
|
|
|
module.modulePath = FilePath::fromString(path);
|
|
|
|
handler->updateModule(module);
|
|
|
|
}
|
|
|
|
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();
|
|
|
|
if (newState == "stopped") {
|
|
|
|
notifyInferiorSpontaneousStop();
|
|
|
|
updateAll();
|
|
|
|
} else if (newState == "inferiorexited") {
|
|
|
|
notifyInferiorExited();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::refreshLocation(const GdbMi &reportedLocation)
|
|
|
|
{
|
|
|
|
StackFrame frame;
|
|
|
|
frame.file = FilePath::fromString(reportedLocation["file"].data());
|
|
|
|
frame.line = reportedLocation["line"].toInt();
|
|
|
|
frame.usable = frame.file.isReadableFile();
|
|
|
|
if (state() == InferiorRunOk) {
|
|
|
|
showMessage(QString("STOPPED AT: %1:%2").arg(frame.file.toUserOutput()).arg(frame.line));
|
|
|
|
gotoLocation(frame);
|
|
|
|
notifyInferiorSpontaneousStop();
|
|
|
|
updateAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::refreshSymbols(const GdbMi &symbols)
|
|
|
|
{
|
|
|
|
QString moduleName = symbols["module"].data();
|
|
|
|
Symbols syms;
|
|
|
|
for (const GdbMi &item : symbols["symbols"]) {
|
|
|
|
Symbol symbol;
|
|
|
|
symbol.name = item["name"].data();
|
|
|
|
syms.append(symbol);
|
|
|
|
}
|
|
|
|
showModuleSymbols(FilePath::fromString(moduleName), syms);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DapEngine::canHandleToolTip(const DebuggerToolTipContext &) const
|
|
|
|
{
|
|
|
|
return state() == InferiorStopOk;
|
|
|
|
}
|
|
|
|
|
2023-04-06 14:20:01 +02:00
|
|
|
void DapEngine::assignValueInDebugger(WatchItem *, const QString &/*expression*/, const QVariant &/*value*/)
|
2023-01-03 15:12:43 +01:00
|
|
|
{
|
|
|
|
//DebuggerCommand cmd("assignValue");
|
|
|
|
//cmd.arg("expression", expression);
|
|
|
|
//cmd.arg("value", value.toString());
|
|
|
|
//runCommand(cmd);
|
2023-06-27 16:57:51 +02:00
|
|
|
// postDirectCommand("global " + expression + ';' + expression + "=" + value.toString());
|
2023-01-03 15:12:43 +01:00
|
|
|
updateLocals();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::updateItem(const QString &iname)
|
|
|
|
{
|
|
|
|
Q_UNUSED(iname)
|
|
|
|
updateAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString DapEngine::errorMessage(QProcess::ProcessError error) const
|
|
|
|
{
|
|
|
|
switch (error) {
|
|
|
|
case QProcess::FailedToStart:
|
|
|
|
return Tr::tr("The DAP process failed to start. Either the "
|
|
|
|
"invoked program \"%1\" is missing, or you may have insufficient "
|
|
|
|
"permissions to invoke the program.")
|
2023-06-27 16:57:51 +02:00
|
|
|
.arg(m_dataGenerator->executable());
|
2023-01-03 15:12:43 +01:00
|
|
|
case QProcess::Crashed:
|
|
|
|
return Tr::tr("The DAP process crashed some time after starting "
|
|
|
|
"successfully.");
|
|
|
|
case QProcess::Timedout:
|
|
|
|
return Tr::tr("The last waitFor...() function timed out. "
|
|
|
|
"The state of QProcess is unchanged, and you can try calling "
|
|
|
|
"waitFor...() again.");
|
|
|
|
case QProcess::WriteError:
|
|
|
|
return Tr::tr("An error occurred when attempting to write "
|
|
|
|
"to the DAP process. For example, the process may not be running, "
|
|
|
|
"or it may have closed its input channel.");
|
|
|
|
case QProcess::ReadError:
|
|
|
|
return Tr::tr("An error occurred when attempting to read from "
|
|
|
|
"the DAP process. For example, the process may not be running.");
|
|
|
|
default:
|
|
|
|
return Tr::tr("An unknown error in the DAP process occurred.") + ' ';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::handleDapDone()
|
|
|
|
{
|
2023-06-27 16:57:51 +02:00
|
|
|
if (m_dataGenerator->result() == ProcessResult::StartFailed) {
|
2023-01-03 15:12:43 +01:00
|
|
|
notifyEngineSetupFailed();
|
|
|
|
showMessage("ADAPTER START FAILED");
|
2023-06-27 16:57:51 +02:00
|
|
|
ICore::showWarningWithOptions(Tr::tr("Adapter start failed"), m_dataGenerator->exitMessage());
|
2023-01-03 15:12:43 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-06-27 16:57:51 +02:00
|
|
|
const QProcess::ProcessError error = m_dataGenerator->error();
|
2023-01-03 15:12:43 +01:00
|
|
|
if (error != QProcess::UnknownError) {
|
|
|
|
showMessage("HANDLE DAP ERROR");
|
|
|
|
if (error != QProcess::Crashed)
|
|
|
|
AsynchronousMessageBox::critical(Tr::tr("DAP I/O Error"), errorMessage(error));
|
|
|
|
if (error == QProcess::FailedToStart)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
showMessage(QString("DAP PROCESS FINISHED, status %1, code %2")
|
2023-06-27 16:57:51 +02:00
|
|
|
.arg(m_dataGenerator->exitStatus()).arg(m_dataGenerator->exitCode()));
|
2023-01-03 15:12:43 +01:00
|
|
|
notifyEngineSpontaneousShutdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::readDapStandardError()
|
|
|
|
{
|
2023-06-27 16:57:51 +02:00
|
|
|
QString err = m_dataGenerator->readAllStandardError();
|
|
|
|
qCDebug(dapEngineLog) << "DAP STDERR:" << err;
|
2023-01-03 15:12:43 +01:00
|
|
|
//qWarning() << "Unexpected DAP stderr:" << err;
|
|
|
|
showMessage("Unexpected DAP stderr: " + err);
|
|
|
|
//handleOutput(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::readDapStandardOutput()
|
|
|
|
{
|
2023-06-27 16:57:51 +02:00
|
|
|
m_inbuffer.append(m_dataGenerator->readAllStandardOutput());
|
|
|
|
|
|
|
|
qCDebug(dapEngineLog) << m_inbuffer;
|
2023-01-03 15:12:43 +01:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
// Something like
|
|
|
|
// Content-Length: 128\r\n
|
|
|
|
// {"type": "event", "event": "output", "body": {"category": "stdout", "output": "...\n"}, "seq": 1}\r\n
|
|
|
|
// FIXME: There coud be more than one header line.
|
|
|
|
int pos1 = m_inbuffer.indexOf("Content-Length:");
|
|
|
|
if (pos1 == -1)
|
|
|
|
break;
|
|
|
|
pos1 += 15;
|
|
|
|
|
|
|
|
int pos2 = m_inbuffer.indexOf('\n', pos1);
|
|
|
|
if (pos2 == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
const int len = m_inbuffer.mid(pos1, pos2 - pos1).trimmed().toInt();
|
|
|
|
if (len < 4)
|
|
|
|
break;
|
|
|
|
|
|
|
|
pos2 += 3; // Skip \r\n\r
|
|
|
|
|
|
|
|
if (pos2 + len > m_inbuffer.size())
|
|
|
|
break;
|
|
|
|
|
|
|
|
QJsonParseError error;
|
|
|
|
const auto doc = QJsonDocument::fromJson(m_inbuffer.mid(pos2, len), &error);
|
|
|
|
|
|
|
|
m_inbuffer = m_inbuffer.mid(pos2 + len);
|
|
|
|
|
|
|
|
handleOutput(doc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::handleOutput(const QJsonDocument &data)
|
|
|
|
{
|
|
|
|
QJsonObject ob = data.object();
|
|
|
|
|
|
|
|
const QJsonValue t = ob.value("type");
|
|
|
|
const QString type = t.toString();
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "response" << ob;
|
2023-01-03 15:12:43 +01:00
|
|
|
|
|
|
|
if (type == "response") {
|
|
|
|
const QString command = ob.value("command").toString();
|
|
|
|
if (command == "configurationDone") {
|
|
|
|
showMessage("configurationDone", LogDebug);
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "configurationDone success";
|
2023-06-16 15:28:50 +02:00
|
|
|
notifyEngineRunAndInferiorRunOk();
|
2023-01-03 15:12:43 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (command == "continue") {
|
|
|
|
showMessage("continue", LogDebug);
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "continue success";
|
2023-01-03 15:12:43 +01:00
|
|
|
notifyInferiorRunOk();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-06-13 17:24:28 +02:00
|
|
|
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();
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "stackTrace success" << file << line;
|
2023-06-13 17:24:28 +02:00
|
|
|
gotoLocation(Location(file, line));
|
2023-06-16 15:28:50 +02:00
|
|
|
|
|
|
|
refreshStack(stackFrames);
|
2023-07-04 16:32:37 +02:00
|
|
|
dapScopes(stackFrame.value("id").toInt());
|
2023-06-13 17:24:28 +02:00
|
|
|
return;
|
|
|
|
}
|
2023-06-15 15:27:33 +02:00
|
|
|
|
2023-06-16 15:28:50 +02:00
|
|
|
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();
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "scoped success" << name << variablesReference;
|
2023-07-13 13:43:59 +02:00
|
|
|
if (name == "Locals") { // Fix for several scopes
|
|
|
|
m_rootWatchItem = new WatchItem();
|
|
|
|
m_currentWatchItem = m_rootWatchItem;
|
|
|
|
watchHandler()->removeAllData();
|
|
|
|
watchHandler()->notifyUpdateStarted();
|
|
|
|
dapVariables(variablesReference);
|
|
|
|
}
|
2023-06-16 15:28:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (command == "variables") {
|
2023-07-13 13:43:59 +02:00
|
|
|
auto variables = ob.value("body").toObject().value("variables").toArray();
|
|
|
|
refreshLocals(variables);
|
2023-06-16 15:28:50 +02:00
|
|
|
}
|
|
|
|
|
2023-06-15 15:27:33 +02:00
|
|
|
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();
|
2023-06-16 15:28:50 +02:00
|
|
|
|
2023-06-15 15:27:33 +02:00
|
|
|
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;
|
|
|
|
}
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "event") {
|
|
|
|
const QString event = ob.value("event").toString();
|
2023-04-06 14:20:01 +02:00
|
|
|
const QJsonObject body = ob.value("body").toObject();
|
|
|
|
|
2023-06-13 17:24:28 +02:00
|
|
|
if (event == "exited") {
|
|
|
|
notifyInferiorExited();
|
|
|
|
showMessage("exited", LogDebug);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
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;
|
|
|
|
}
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << data;
|
2023-01-03 15:12:43 +01:00
|
|
|
|
|
|
|
if (event == "initialized") {
|
|
|
|
showMessage(event, LogDebug);
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "initialize success";
|
2023-06-16 15:28:50 +02:00
|
|
|
handleDapLaunch();
|
|
|
|
handleDapConfigurationDone();
|
2023-01-03 15:12:43 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event == "stopped") {
|
2023-06-13 17:24:28 +02:00
|
|
|
m_currentThreadId = body.value("threadId").toInt();
|
2023-04-06 14:20:01 +02:00
|
|
|
showMessage(event, LogDebug);
|
|
|
|
if (body.value("reason").toString() == "breakpoint") {
|
2023-06-13 17:24:28 +02:00
|
|
|
QString id = QString::number(
|
|
|
|
body.value("hitBreakpointIds").toArray().first().toInteger());
|
2023-06-27 16:57:51 +02:00
|
|
|
|
|
|
|
Breakpoint bp = breakHandler()->findBreakpointByResponseId(id);
|
|
|
|
if (bp) {
|
|
|
|
const BreakpointParameters ¶ms = bp->requestedParameters();
|
|
|
|
gotoLocation(Location(params.fileName, params.textPosition));
|
|
|
|
}
|
2023-04-06 14:20:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (state() == InferiorStopRequested)
|
|
|
|
notifyInferiorStopOk();
|
|
|
|
else
|
|
|
|
notifyInferiorSpontaneousStop();
|
2023-06-16 15:28:50 +02:00
|
|
|
|
|
|
|
dapStackTrace();
|
2023-06-15 15:27:33 +02:00
|
|
|
threads();
|
2023-01-03 15:12:43 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-04-06 14:20:01 +02:00
|
|
|
if (event == "thread") {
|
2023-06-27 16:57:51 +02:00
|
|
|
// threads(); // breaks cmake debugging for now
|
2023-06-15 15:27:33 +02:00
|
|
|
|
2023-04-06 14:20:01 +02:00
|
|
|
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()));
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "breakpoint id :" << breakpoint.value("id").toInt();
|
2023-04-06 14:20:01 +02:00
|
|
|
|
|
|
|
if (body.value("reason").toString() == "new") {
|
|
|
|
if (breakpoint.value("verified").toBool()) {
|
|
|
|
notifyBreakpointInsertOk(bp);
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "breakpoint inserted";
|
2023-04-06 14:20:01 +02:00
|
|
|
} else {
|
|
|
|
notifyBreakpointInsertFailed(bp);
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "breakpoint insertion failed";
|
2023-04-06 14:20:01 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body.value("reason").toString() == "removed") {
|
|
|
|
if (breakpoint.value("verified").toBool()) {
|
|
|
|
notifyBreakpointRemoveOk(bp);
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "breakpoint removed";
|
2023-04-06 14:20:01 +02:00
|
|
|
} else {
|
|
|
|
notifyBreakpointRemoveFailed(bp);
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "breakpoint remove failed";
|
2023-04-06 14:20:01 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-01-03 15:12:43 +01:00
|
|
|
showMessage("UNKNOWN EVENT:" + event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
showMessage("UNKNOWN TYPE:" + type);
|
|
|
|
}
|
|
|
|
|
2023-06-16 15:28:50 +02:00
|
|
|
void DapEngine::refreshLocals(const QJsonArray &variables)
|
|
|
|
{
|
|
|
|
for (auto variable : variables) {
|
|
|
|
WatchItem *item = new WatchItem;
|
|
|
|
const QString name = variable.toObject().value("name").toString();
|
|
|
|
item->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();
|
2023-07-13 13:43:59 +02:00
|
|
|
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "variable" << name << item->hexAddress();
|
2023-07-13 13:43:59 +02:00
|
|
|
m_currentWatchItem->appendChild(item);
|
|
|
|
|
|
|
|
const int variablesReference = variable.toObject().value("variablesReference").toInt();
|
|
|
|
if (variablesReference > 0)
|
|
|
|
m_variablesReferenceQueue.push({variablesReference, item});
|
2023-06-16 15:28:50 +02:00
|
|
|
}
|
2023-07-13 13:43:59 +02:00
|
|
|
|
|
|
|
if (m_variablesReferenceQueue.empty()) {
|
|
|
|
for (auto item = m_rootWatchItem->begin(); item != m_rootWatchItem->end(); ++item)
|
|
|
|
watchHandler()->insertItem(*item);
|
|
|
|
watchHandler()->notifyUpdateFinished();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto front = m_variablesReferenceQueue.front();
|
|
|
|
m_variablesReferenceQueue.pop();
|
|
|
|
|
|
|
|
dapVariables(front.first);
|
|
|
|
m_currentWatchItem = front.second;
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
2023-06-16 15:28:50 +02:00
|
|
|
void DapEngine::refreshStack(const QJsonArray &stackFrames)
|
2023-01-03 15:12:43 +01:00
|
|
|
{
|
|
|
|
StackHandler *handler = stackHandler();
|
|
|
|
StackFrames frames;
|
2023-06-16 15:28:50 +02:00
|
|
|
for (const auto &value : stackFrames) {
|
2023-01-03 15:12:43 +01:00
|
|
|
StackFrame frame;
|
2023-06-16 15:28:50 +02:00
|
|
|
QJsonObject item = value.toObject();
|
|
|
|
frame.level = item.value("id").toString();
|
|
|
|
frame.function = item.value("name").toString();
|
|
|
|
frame.line = item.value("line").toInt();
|
|
|
|
QJsonObject source = item.value("source").toObject();
|
|
|
|
frame.file = FilePath::fromString(source.value("path").toString());
|
|
|
|
frame.address = item.value("instructionPointerReference").toInt();
|
|
|
|
frame.usable = frame.file.isReadableFile();
|
2023-01-03 15:12:43 +01:00
|
|
|
frames.append(frame);
|
|
|
|
}
|
2023-06-16 15:28:50 +02:00
|
|
|
handler->setFrames(frames, false);
|
2023-01-03 15:12:43 +01:00
|
|
|
|
|
|
|
int index = stackHandler()->firstUsableIndex();
|
|
|
|
handler->setCurrentIndex(index);
|
|
|
|
if (index >= 0 && index < handler->stackSize())
|
|
|
|
gotoLocation(handler->frameAt(index));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::updateAll()
|
|
|
|
{
|
|
|
|
runCommand({"stackListFrames"});
|
|
|
|
updateLocals();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::updateLocals()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DapEngine::hasCapability(unsigned cap) const
|
|
|
|
{
|
|
|
|
return cap & (ReloadModuleCapability
|
2023-06-27 16:57:51 +02:00
|
|
|
| BreakConditionCapability
|
|
|
|
| ShowModuleSymbolsCapability);
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DapEngine::claimInitialBreakpoints()
|
|
|
|
{
|
|
|
|
BreakpointManager::claimBreakpointsForEngine(this);
|
2023-06-27 16:57:51 +02:00
|
|
|
qCDebug(dapEngineLog) << "claimInitialBreakpoints";
|
2023-01-03 15:12:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DebuggerEngine *createDapEngine()
|
|
|
|
{
|
|
|
|
return new DapEngine;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // Debugger::Internal
|