diff --git a/src/base/abstractdebugger.h b/src/base/abstractdebugger.h index 240694294..b8e7c4cef 100644 --- a/src/base/abstractdebugger.h +++ b/src/base/abstractdebugger.h @@ -52,14 +52,17 @@ class AbstractDebugger : public QObject virtual void interruptDebug() = 0; virtual void continueDebug() = 0; + virtual void reverseContinue() = 0; virtual void abortDebug() = 0; virtual void restartDebug() = 0; virtual void stepOver() = 0; virtual void stepIn() = 0; virtual void stepOut() = 0; + virtual void stepBack() = 0; virtual RunState getRunState() const = 0; + virtual bool supportStepBack() = 0; virtual bool runCoredump(const QString &target, const QString &core, const QString &kit) = 0; signals: diff --git a/src/common/actionmanager/action_define.h b/src/common/actionmanager/action_define.h index cefe6a558..d11c49094 100644 --- a/src/common/actionmanager/action_define.h +++ b/src/common/actionmanager/action_define.h @@ -15,6 +15,7 @@ constexpr char M_BUILD[] = "IDE.Menu.Build"; constexpr char M_DEBUG[] = "IDE.Menu.Debug"; constexpr char M_TOOLS[] = "IDE.Menu.Tools"; constexpr char M_TOOLS_BINARY[] = "IDE.Menu.Tools.Binary"; +constexpr char M_TOOLS_EVENTRECORDER[] = "IDE.Menu.Tools.EventRecorder"; constexpr char M_TOOLS_REVERSEDEBUG[] = "IDE.Menu.Tools.ReverseDebug"; constexpr char M_HELP[] = "IDE.Menu.Help"; diff --git a/src/plugins/debugger/dap/dapdebugger.cpp b/src/plugins/debugger/dap/dapdebugger.cpp index d770ae1f9..901101f74 100644 --- a/src/plugins/debugger/dap/dapdebugger.cpp +++ b/src/plugins/debugger/dap/dapdebugger.cpp @@ -117,7 +117,7 @@ class DebuggerPrivate bool isRemote = false; RemoteInfo remoteInfo; - bool startAttaching = false; + DAPDebugger::debugState debugState = DAPDebugger::Normal; }; DebuggerPrivate::~DebuggerPrivate() @@ -215,6 +215,35 @@ void DAPDebugger::startDebug() } } +void DAPDebugger::startRerverseDebug(const QString &target) +{ + d->isRemote = false; + d->debugState = Reverse; + if (d->currentSession == d->remoteSession) + d->currentSession = d->localSession; + + updateRunState(kPreparing); + + QMap param; + param.insert("program", "rr"); + + d->requestDAPPortPpid = QString(getpid()); + QDBusMessage msg = QDBusMessage::createSignal("/path", + "com.deepin.unioncode.interface", + "getDebugPort"); + + msg << d->requestDAPPortPpid + << "cmake" //rr only support c/c++ + << "" + << QStringList{target}; + + bool ret = QDBusConnection::sessionBus().send(msg); + if (!ret) { + qWarning() << "requeset debug port failed"; + updateRunState(kNoRun); + } +} + void DAPDebugger::startDebugRemote(const RemoteInfo &info) { d->remoteInfo = info; @@ -255,7 +284,7 @@ void DAPDebugger::attachDebug(const QString &processId) } d->isRemote = false; - d->startAttaching = true; + d->debugState = Attaching; d->currentSession = d->localSession; // only support gdb for now @@ -304,6 +333,14 @@ void DAPDebugger::continueDebug() } } +void DAPDebugger::reverseContinue() +{ + if (d->runState == kStopped) { + d->currentSession->reverseContinue(d->threadId); + editor.removeDebugLine(); + } +} + void DAPDebugger::abortDebug() { if (d->runState == kRunning || d->runState == kStopped || d->runState == kCustomRunning) { @@ -321,8 +358,6 @@ void DAPDebugger::abortDebug() } d->currentSession->terminate(); - if (d->startAttaching) - d->startAttaching = false; printOutput(tr("\nThe debugee has Terminated.\n"), OutputPane::OutputFormat::NormalMessage); } @@ -366,6 +401,18 @@ void DAPDebugger::stepOut() } } +void DAPDebugger::stepBack() +{ + if (d->runState == kStopped && d->processingVariablesCount == 0) { + d->currentSession->stepBack(d->threadId, undefined); + } +} + +bool DAPDebugger::supportStepBack() +{ + return d->debugState == Reverse; +} + DAPDebugger::RunState DAPDebugger::getRunState() const { return d->runState; @@ -568,17 +615,18 @@ void DAPDebugger::registerDapHandlers() bool signalStopped = false; if (event.reason == "signal-received" && event.description.has_value()) { + signalStopped = true; auto signalName = QString::fromStdString(event.description.value().c_str()); if (signalName == "SIGSEGV") { // Segmentation fault - signalStopped = true; auto signalMeaning = event.text.has_value() ? event.text.value().c_str() : ""; QMetaObject::invokeMethod(this, "showStoppedBySignalMessageBox", Q_ARG(QString, QString::fromStdString(signalMeaning)), Q_ARG(QString, signalName)); } - if (signalName == "SIGINT" && d->pausing) // stopped by user - signalStopped = true; + if (signalName == "SIGINT" && !d->pausing) // stopped by user + signalStopped = false; } + bool attaching = d->debugState == Attaching; // ui focus on the active frame. if (event.reason == "function breakpoint" || event.reason == "breakpoint" @@ -588,14 +636,12 @@ void DAPDebugger::registerDapHandlers() || event.reason == "end-stepping-range" || event.reason == "goto" || signalStopped - || (event.reason == "unknown" && d->startAttaching)) { + || (event.reason == "unknown" && attaching)) { //when attaching to running program . it won`t receive initialized event and event`s reason is "unknwon" //so initial breakpoints in here - if (d->startAttaching) { + if (attaching) { d->currentSession->getRawSession()->setReadyForBreakpoints(true); debugService->sendAllBreakpoints(d->currentSession); - - d->startAttaching = false; } if (event.threadId) { d->threadId = event.threadId.value(0); @@ -1265,6 +1311,7 @@ void DAPDebugger::updateRunState(DAPDebugger::RunState state) case kNoRun: exitDebug(); AppOutputPane::instance()->setProcessFinished("debugPane"); + d->debugState = Normal; break; case kRunning: case kCustomRunning: @@ -1333,7 +1380,7 @@ void DAPDebugger::prepareDebug() QMap param; if (!d->isRemote) param = generator->getDebugArguments(getActiveProjectInfo(), d->currentOpenedFileName); - + bool ret = generator->prepareDebug(param, retMsg); if (!ret) { printOutput(retMsg, OutputPane::ErrorMessage); @@ -1448,31 +1495,56 @@ void DAPDebugger::launchSession(int port, const QMap ¶m, } // Launch debuggee. - if (d->startAttaching) { - dap::object obj; - obj["processId"] = param.value("targetPath").toString().toStdString(); - dap::AttachRequest request; - request.name = kitName.toStdString(); //kitName: gdb - request.connect = obj; - bSuccess &= d->currentSession->attach(request); - } else { - auto &ctx = dpfInstance.serviceContext(); - LanguageService *service = ctx.service(LanguageService::name()); - if (service) { - auto generator = service->create(kitName); - if (generator) { - if (generator->isLaunchNotAttach()) { - dap::LaunchRequest request = generator->launchDAP(param); - bSuccess &= d->currentSession->launch(request); - } else { - dap::AttachRequest request = generator->attachDAP(port, param); - bSuccess &= d->currentSession->attach(request); + switch (d->debugState) { + case Normal: { + auto &ctx = dpfInstance.serviceContext(); + LanguageService *service = ctx.service(LanguageService::name()); + if (service) { + auto generator = service->create(kitName); + if (generator) { + if (generator->isLaunchNotAttach()) { + dap::LaunchRequest request = generator->launchDAP(param); + bSuccess &= d->currentSession->launch(request); + } else { + dap::AttachRequest request = generator->attachDAP(port, param); + bSuccess &= d->currentSession->attach(request); + } } + } else { + bSuccess &= false; } - } else { - bSuccess &= false; + break; } + case Attaching: { + dap::object obj; + obj["processId"] = param.value("targetPath").toString().toStdString(); + dap::AttachRequest request; + request.name = kitName.toStdString(); //kitName: gdb + request.connect = obj; + bSuccess &= d->currentSession->attach(request); + break; + } + case Reverse: { + dap::LaunchRequest request; + request.name = "rr"; + request.type = "cppdbg"; + request.request = "launch"; + request.program = ""; // targetPath.toStdString(); + request.stopAtEntry = false; + dap::array arrayArg; + foreach (QString arg, param["arguments"].toStringList()) { + arrayArg.push_back(arg.toStdString()); + } + request.args = arrayArg; + request.externalConsole = false; + request.MIMode = "gdb"; + request.__sessionId = QUuid::createUuid().toString().toStdString(); + bSuccess &= d->currentSession->launch(request); + } + default: + break; } + if (!bSuccess) { qCritical() << "startDebug failed!"; } else { diff --git a/src/plugins/debugger/dap/dapdebugger.h b/src/plugins/debugger/dap/dapdebugger.h index c528b5029..34739c537 100644 --- a/src/plugins/debugger/dap/dapdebugger.h +++ b/src/plugins/debugger/dap/dapdebugger.h @@ -45,6 +45,12 @@ class DAPDebugger : public AbstractDebugger explicit DAPDebugger(QObject *parent = nullptr); ~DAPDebugger() override; + enum debugState { + Normal, + Attaching, + Reverse + }; + DWidget *getOutputPane() const override; DWidget *getStackPane() const override; DWidget *getLocalsPane() const override; @@ -53,18 +59,22 @@ class DAPDebugger : public AbstractDebugger void startDebug() override; void startDebugRemote(const RemoteInfo &info) override; + void startRerverseDebug(const QString &target); void attachDebug(const QString &processId) override; void detachDebug() override; void interruptDebug() override; void continueDebug() override; + void reverseContinue() override; void abortDebug() override; void restartDebug() override; void stepOver() override; void stepIn() override; void stepOut() override; + void stepBack() override; + bool supportStepBack() override; RunState getRunState() const override; bool runCoredump(const QString &target, const QString &core, const QString &kit) override; diff --git a/src/plugins/debugger/debugmanager.cpp b/src/plugins/debugger/debugmanager.cpp index 5292171c9..3ce1f3db3 100644 --- a/src/plugins/debugger/debugmanager.cpp +++ b/src/plugins/debugger/debugmanager.cpp @@ -8,6 +8,7 @@ #include "debuggerglobals.h" #include "interface/menumanager.h" #include "interface/attachinfodialog.h" +#include "reversedebug/reversedebugger.h" #include "services/debugger/debuggerservice.h" #include "services/window/windowservice.h" @@ -34,6 +35,9 @@ bool DebugManager::initialize(dpfservice::WindowService *windowService, menuManager.reset(new MenuManager()); menuManager->initialize(windowService); + auto reverseDbg = new ReverseDebugger(this); + connect(reverseDbg, &ReverseDebugger::startReplay, this, &DebugManager::rrReplay); + connect(currentDebugger, &AbstractDebugger::runStateChanged, this, &DebugManager::handleRunStateChanged); // bind debug services @@ -170,6 +174,11 @@ void DebugManager::continueDebug() AsynInvoke(currentDebugger->continueDebug()); } +void DebugManager::reverseContinue() +{ + AsynInvoke(currentDebugger->reverseContinue()); +} + void DebugManager::abortDebug() { AsynInvoke(currentDebugger->abortDebug()); @@ -198,11 +207,21 @@ void DebugManager::stepOut() AsynInvoke(currentDebugger->stepOut()); } +void DebugManager::stepBack() +{ + AsynInvoke(currentDebugger->stepBack()); +} + +bool DebugManager::supportStepBack() +{ + return currentDebugger->supportStepBack(); +} + void DebugManager::handleRunStateChanged(AbstractDebugger::RunState state) { menuManager->handleRunStateChanged(state); - if(state == AbstractDebugger::kStart || state == AbstractDebugger::kRunning) { + if(state == AbstractDebugger::kStart || state == AbstractDebugger::kRunning || state == AbstractDebugger::kCustomRunning) { emit debugStarted(); } else if (state == AbstractDebugger::kNoRun) { emit debugStopped(); @@ -236,3 +255,10 @@ bool DebugManager::runCoredump(const QString &target, const QString &core, const { return QtConcurrent::run(currentDebugger, &AbstractDebugger::runCoredump, target, core, kit); } + +void DebugManager::rrReplay(const QString &target) +{ + auto dapdbgger = qobject_cast(debuggers["dap"]); + dapdbgger->startRerverseDebug(target); +} + diff --git a/src/plugins/debugger/debugmanager.h b/src/plugins/debugger/debugmanager.h index 2528c872e..d68191d5b 100644 --- a/src/plugins/debugger/debugmanager.h +++ b/src/plugins/debugger/debugmanager.h @@ -56,18 +56,22 @@ public slots: void interruptDebug(); void continueDebug(); + void reverseContinue(); void abortDebug(); void restartDebug(); void stepOver(); void stepIn(); void stepOut(); + void stepBack(); + bool supportStepBack(); void handleRunStateChanged(AbstractDebugger::RunState state); void handleEvents(const dpf::Event &event); private: bool runCoredump(const QString &target, const QString &core, const QString &kit); + void rrReplay(const QString &target); QMap debuggers; AbstractDebugger *currentDebugger = nullptr; diff --git a/src/plugins/debugger/interface/menumanager.cpp b/src/plugins/debugger/interface/menumanager.cpp index 56c234c09..0ea515cbb 100644 --- a/src/plugins/debugger/interface/menumanager.cpp +++ b/src/plugins/debugger/interface/menumanager.cpp @@ -118,6 +118,24 @@ void MenuManager::initialize(WindowService *windowService) mDebug->addAction(actionImpl); appOutPutPane->registerItemToToolBar(debugToolBarName, actionImpl->action(), false); + stepBack.reset(new QAction(MWMDA_STEP_BACK)); + stepBack->setEnabled(false); + connect(stepBack.get(), &QAction::triggered, debugManager, &DebugManager::stepBack); + actionImpl = initAction(stepBack.get(), "Debug.Step.Back", + MWMDA_STEP_BACK, QKeySequence(), + "debugger_stepback"); + mDebug->addAction(actionImpl); + appOutPutPane->registerItemToToolBar(debugToolBarName, actionImpl->action(), false); + + reverseContinue.reset(new QAction(MWMDA_REVERSE_CONTINUE)); + reverseContinue->setEnabled(false); + connect(reverseContinue.get(), &QAction::triggered, debugManager, &DebugManager::reverseContinue); + actionImpl = initAction(reverseContinue.get(), "Debug.Reverse.Continue", + MWMDA_REVERSE_CONTINUE, QKeySequence(), + "debugger_reverse_continue"); + mDebug->addAction(actionImpl); + appOutPutPane->registerItemToToolBar(debugToolBarName, actionImpl->action(), false); + remoteDebug.reset(new QAction(MWMDA_REMOTE_DEBUG)); connect(remoteDebug.get(), &QAction::triggered, debugManager, [=]() { auto remoteDlg = new RemoteDebugDlg(); @@ -154,6 +172,8 @@ void MenuManager::handleRunStateChanged(AbstractDebugger::RunState state) stepOver->setEnabled(false); stepIn->setEnabled(false); stepOut->setEnabled(false); + stepBack->setEnabled(false); + reverseContinue->setEnabled(false); attachDebugging->setEnabled(true); break; @@ -169,6 +189,8 @@ void MenuManager::handleRunStateChanged(AbstractDebugger::RunState state) stepOver->setEnabled(false); stepIn->setEnabled(false); stepOut->setEnabled(false); + stepBack->setEnabled(false); + reverseContinue->setEnabled(false); attachDebugging->setEnabled(false); break; case AbstractDebugger::kStopped: @@ -183,6 +205,8 @@ void MenuManager::handleRunStateChanged(AbstractDebugger::RunState state) stepOver->setEnabled(true); stepIn->setEnabled(true); stepOut->setEnabled(true); + stepBack->setEnabled(true & debugManager->supportStepBack()); + reverseContinue->setEnabled(true & debugManager->supportStepBack()); attachDebugging->setEnabled(false); break; case AbstractDebugger::kCustomRunning: @@ -194,6 +218,8 @@ void MenuManager::handleRunStateChanged(AbstractDebugger::RunState state) stepOver->setEnabled(false); stepIn->setEnabled(false); stepOut->setEnabled(false); + stepBack->setEnabled(false); + reverseContinue->setEnabled(false); attachDebugging->setEnabled(false); break; diff --git a/src/plugins/debugger/interface/menumanager.h b/src/plugins/debugger/interface/menumanager.h index 56143171d..4680d9018 100644 --- a/src/plugins/debugger/interface/menumanager.h +++ b/src/plugins/debugger/interface/menumanager.h @@ -35,11 +35,13 @@ public slots: QSharedPointer detachDebugger; QSharedPointer interrupt; QSharedPointer continueDebugging; + QSharedPointer reverseContinue; QSharedPointer abortDebugging; QSharedPointer restartDebugging; QSharedPointer stepOver; QSharedPointer stepIn; QSharedPointer stepOut; + QSharedPointer stepBack; QSharedPointer remoteDebug; }; diff --git a/src/plugins/debugger/resource.qrc b/src/plugins/debugger/resource.qrc index 2617bfb33..41da7318f 100644 --- a/src/plugins/debugger/resource.qrc +++ b/src/plugins/debugger/resource.qrc @@ -3,10 +3,12 @@ icons/executable_16px.svg texts/debug-navigation_22px.svg texts/debugger_continue_16px.svg + texts/debugger_reverse_continue_16px.svg texts/debugger_interrupt_16px.svg texts/debugger_stepinto_16px.svg texts/debugger_stepout_16px.svg texts/debugger_stepover_16px.svg + texts/debugger_stepback_16px.svg texts/restart_debug_16px.svg texts/variable_watchers_16px.svg light/icons/breakpoint_14px.svg diff --git a/src/plugins/debugger/reversedebug/reversedebugger.cpp b/src/plugins/debugger/reversedebug/reversedebugger.cpp new file mode 100644 index 000000000..4eaac130f --- /dev/null +++ b/src/plugins/debugger/reversedebug/reversedebugger.cpp @@ -0,0 +1,181 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "reversedebugger.h" +#include "rrdialog.h" +#include "common/actionmanager/actionmanager.h" +#include "common/actionmanager/actioncontainer.h" +#include "services/window/windowservice.h" +#include "services/window/windowelement.h" +#include "services/terminal/terminalservice.h" +#include "services/language/languageservice.h" + +#include +#include +#include + +constexpr char A_REVERSE_DEBUG_RECORD[] = "RverseDebug.Action.Record"; +constexpr char A_REVERSE_DEBUG_REPLAY[] = "RverseDebug.Action.Replay"; + +ReverseDebugger::ReverseDebugger(QObject *parent) + : QObject(parent) +{ + init(); +} + +void ReverseDebugger::init() +{ + auto mTools = ActionManager::instance()->actionContainer(M_TOOLS); + auto mReverseDbg = ActionManager::instance()->createContainer(M_TOOLS_REVERSEDEBUG); + mReverseDbg->menu()->setTitle(tr("Reverse debug")); + mTools->addMenu(mReverseDbg); + + auto actionInit = [&](QAction *action, QString actionID) { + auto cmd = ActionManager::instance()->registerAction(action, actionID); + mReverseDbg->addAction(cmd); + }; + + auto recoredAction = new QAction(tr("Record"), mReverseDbg); + actionInit(recoredAction, A_REVERSE_DEBUG_RECORD); + auto replayAction = new QAction(tr("Replay"), mReverseDbg); + actionInit(replayAction, A_REVERSE_DEBUG_REPLAY); + + connect(recoredAction, &QAction::triggered, this, &ReverseDebugger::record); + connect(replayAction, &QAction::triggered, this, &ReverseDebugger::replay); + connect(this, &ReverseDebugger::recordDone, this, [=]() { + QMessageBox::information(nullptr, tr("Reverse Debug"), tr("Recored done.")); + }); + connect(this, &ReverseDebugger::recordFailed, this, [=](const QString &err) { + QMessageBox::warning(nullptr, tr("Reverse Debug"), tr("Recored Failed.\n%1").arg(err)); + }); +} + +void ReverseDebugger::record() +{ + if (!checkRRInstalled()) + return; + RecordDialog dialog; + connect(&dialog, &RecordDialog::startRecord, this, [=](const dpfservice::RunCommandInfo &args) { + QtConcurrent::run([=]() { + execCommand(args); + }); + }); + dialog.exec(); +} + +void ReverseDebugger::replay() +{ + if (!checkRRInstalled()) + return; + ReplayDialog dialog; + connect(&dialog, &ReplayDialog::startReplay, this, &ReverseDebugger::startReplay); + dialog.exec(); +} + +bool ReverseDebugger::execCommand(const dpfservice::RunCommandInfo &info) +{ + bool ret = false; + + auto target = info.program; + if (target.isEmpty() || !QFile::exists(target)) { + emit recordFailed(tr("target not found!")); + return ret; + } + + QProcess process; + process.setWorkingDirectory(info.workingDir); + process.setEnvironment(info.envs); + + auto program = "rr"; + QStringList arguments; + arguments.append("record"); + arguments.append(info.program); + arguments.append(info.arguments); + + connect(&process, &QProcess::readyReadStandardOutput, [&]() { + process.setReadChannel(QProcess::StandardOutput); + while (process.canReadLine()) { + QString line = QString::fromUtf8(process.readLine()); + outputMsg(process.pid(), line, OutputPane::OutputFormat::StdOut); + } + }); + + connect(&process, &QProcess::readyReadStandardError, [&]() { + process.setReadChannel(QProcess::StandardError); + while (process.canReadLine()) { + QString line = QString::fromUtf8(process.readLine()); + outputMsg(process.pid(), line, OutputPane::OutputFormat::StdErr); + } + }); + + process.start(program, arguments); + uiController.switchContext(tr("&Application Output")); + quint64 pid = process.pid(); + QString startMsg = tr("Start execute command: \"%1\" \"%2\" in workspace \"%3\".\n") + .arg(program, arguments.join(" "), info.workingDir); + QMetaObject::invokeMethod(AppOutputPane::instance(), "createApplicationPane", + Q_ARG(const QString &, QString::number(pid)), Q_ARG(QString, program)); + outputMsg(pid, startMsg, OutputPane::OutputFormat::NormalMessage); + process.waitForFinished(); + + AppOutputPane::instance()->setProcessFinished(QString::number(pid)); + + emit recordDone(); + + return ret; +} + +void ReverseDebugger::outputMsg(const quint64 &pid, const QString &content, OutputPane::OutputFormat format) +{ + QMetaObject::invokeMethod(this, "synOutputMsg", Q_ARG(quint64, pid), Q_ARG(QString, content), Q_ARG(OutputPane::OutputFormat, format)); +} + +void ReverseDebugger::synOutputMsg(const quint64 &pid, const QString &content, OutputPane::OutputFormat format) +{ + auto outputPane = AppOutputPane::instance()->getOutputPaneById(QString::number(pid)); + QString outputContent = content; + if (format == OutputPane::OutputFormat::NormalMessage) { + QTextDocument *doc = outputPane->document(); + QTextBlock tb = doc->lastBlock(); + QString lastLineText = tb.text(); + QString prefix = "\n"; + if (lastLineText.isEmpty()) { + prefix = ""; + } + QDateTime curDatetime = QDateTime::currentDateTime(); + QString time = curDatetime.toString("hh:mm:ss"); + outputContent = prefix + time + ":" + content + "\n"; + } + OutputPane::AppendMode mode = OutputPane::AppendMode::Normal; + outputPane->appendText(outputContent, format, mode); +} + +void installRR(const QString &id) +{ + if (id == "apt") { + auto windowService = dpfGetService(dpfservice::WindowService); + windowService->installPackages(ReverseDebugger::tr("Reverse debugger"), "apt", { "rr" }, nullptr); + } else if (id == "wget") { + auto terminalService = dpfGetService(dpfservice::TerminalService); + auto command = "cd /tmp && wget https://github.com/rr-debugger/rr/releases/download/5.5.0/rr-5.5.0-Linux-x86_64.deb && sudo dpkg -i rr-5.5.0-Linux-x86_64.deb"; + uiController.switchContext(dpfservice::TERMINAL_TAB_TEXT); + terminalService->sendCommand(command); + } +} + +bool ReverseDebugger::checkRRInstalled() +{ + QProcess process; + process.start("rr"); + + if (process.waitForStarted()) { + process.close(); + return true; + } else { + auto windowService = dpfGetService(dpfservice::WindowService); + windowService->notifyWithCallback(0, tr("Reverse Debug"), tr("Can not find rr debugger, please install it first"), QStringList{"apt", tr("Install by apt"), "wget", tr("Install by Wget")}, installRR); + process.close(); + return false; + } +} diff --git a/src/plugins/debugger/reversedebug/reversedebugger.h b/src/plugins/debugger/reversedebug/reversedebugger.h new file mode 100644 index 000000000..dfd9a0097 --- /dev/null +++ b/src/plugins/debugger/reversedebug/reversedebugger.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef REVERSEDEBUGGER_H +#define REVERSEDEBUGGER_H + +#include "services/language/languageservice.h" +#include "common/widget/appoutputpane.h" + +#include + +class ReverseDebugger : public QObject +{ + Q_OBJECT +public: + explicit ReverseDebugger(QObject *parent = nullptr); + + void init(); + +public slots: + void synOutputMsg(const quint64 &pid, const QString &content, OutputPane::OutputFormat format = OutputPane::OutputFormat::NormalMessage); + +signals: + void startReplay(const QString &target); + void recordDone(); + void recordFailed(const QString &err); + +private: + bool checkRRInstalled(); + void record(); + void replay(); + + void outputMsg(const quint64 &pid, const QString &content, OutputPane::OutputFormat format = OutputPane::OutputFormat::NormalMessage); + + bool execCommand(const dpfservice::RunCommandInfo &args); +}; + +#endif // REVERSEDEBUGGER_H diff --git a/src/plugins/debugger/reversedebug/rrdialog.cpp b/src/plugins/debugger/reversedebug/rrdialog.cpp new file mode 100644 index 000000000..0d7596807 --- /dev/null +++ b/src/plugins/debugger/reversedebug/rrdialog.cpp @@ -0,0 +1,181 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rrdialog.h" +#include "debugmanager.h" +#include "services/project/projectservice.h" +#include "services/language/languageservice.h" +#include "services/window/windowelement.h" +#include "debuggerglobals.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +DWIDGET_USE_NAMESPACE +using namespace dpfservice; +RecordDialog::RecordDialog(QWidget *parent) : + DDialog(parent) +{ + setupUi(); +} + +RecordDialog::~RecordDialog() +{ +} + +void RecordDialog::on_pbtnOK_clicked() +{ + auto prjService = dpfGetService(ProjectService); + auto langService = dpfGetService(LanguageService); + auto activePrjInfo = prjService->getActiveProjectInfo(); + auto generator = langService->create(activePrjInfo.kitName()); + RunCommandInfo args = generator->getRunArguments(activePrjInfo, ""); + + emit startRecord(args); + accept(); +} + +void RecordDialog::on_pbtnCancel_clicked() +{ + reject(); +} + +void RecordDialog::setupUi() +{ + setWindowTitle(tr("Record")); + resize(400, 196); + DWidget *mainFrame = new DWidget(this); + auto verticalLayout = new QVBoxLayout(mainFrame); + addContent(mainFrame); + verticalLayout->setSpacing(10); + verticalLayout->setContentsMargins(11, 11, 11, 11); + + auto contentLayout = new QFormLayout; + QLabel *projectLb = new QLabel(tr("Current Project:"), this); + QComboBox *projectCb = new QComboBox(this); + auto prjService = dpfGetService(ProjectService); + ProjectInfo activePrjInfo = prjService->getActiveProjectInfo(); + projectCb->addItem(activePrjInfo.kitName()); + contentLayout->addRow(projectLb, projectCb); + + QLabel *programLb = new QLabel(tr("Target:"), this); + QLabel *programCb = new QLabel(activePrjInfo.currentProgram(), this); + contentLayout->addRow(programLb, programCb); + + verticalLayout->addLayout(contentLayout); + + // init button + auto horizontalLayout = new QHBoxLayout(); + horizontalLayout->setSpacing(6); + + auto pbtnCancel = new DPushButton(this); + pbtnCancel->setText("Cancel"); + pbtnCancel->setObjectName(QStringLiteral("pbtnCancel")); + horizontalLayout->addWidget(pbtnCancel); + + auto pbtnOK = new DSuggestButton(this); + pbtnOK->setText(tr("Start Record")); + pbtnOK->setObjectName(QStringLiteral("pbtnOK")); + horizontalLayout->addWidget(pbtnOK); + + if (activePrjInfo.language() != MWMFA_CXX) + pbtnOK->setEnabled(false); + + QLabel *message = new QLabel(tr("Only support C/C++, and some architectures of CPU may experience anomalies."), this); + message->setWordWrap(true); + QPalette palette = message->palette(); + palette.setColor(QPalette::WindowText, Qt::red); + message->setPalette(palette); + + verticalLayout->addWidget(message); + verticalLayout->addLayout(horizontalLayout); + + QMetaObject::connectSlotsByName(this); +} + +ReplayDialog::ReplayDialog(QWidget *parent) : + DDialog(parent) +{ + setupUi(); +} + +ReplayDialog::~ReplayDialog() +{ +} + +void ReplayDialog::on_pbtnOK_clicked() +{ + emit startReplay(traceDir->text()); + accept(); +} + +void ReplayDialog::on_pbtnCancel_clicked() +{ + reject(); +} + +void ReplayDialog::setupUi() +{ + setWindowTitle(tr("Replay")); + resize(400, 196); + DWidget *mainFrame = new DWidget(this); + auto verticalLayout = new QVBoxLayout(mainFrame); + addContent(mainFrame); + verticalLayout->setSpacing(10); + verticalLayout->setContentsMargins(11, 11, 11, 11); + + QString defaultTraceDir = QDir::homePath() + QDir::separator() + ".local/share/rr/latest-trace"; + + auto contentLayout = new QHBoxLayout; + auto *traceLb = new QLabel(tr("Trace Directory:"), this); + traceDir = new DLineEdit(this); + traceDir->setText(defaultTraceDir); + DSuggestButton *btnBrowser = new DSuggestButton(this); + btnBrowser->setIcon(DStyle::standardIcon(style(), DStyle::SP_SelectElement)); + btnBrowser->setIconSize(QSize(24, 24)); + btnBrowser->setFixedSize(36, 36); + connect(btnBrowser, &DSuggestButton::clicked, this, &ReplayDialog::showFileDialog); + + contentLayout->addWidget(traceLb); + contentLayout->addWidget(traceDir); + contentLayout->addWidget(btnBrowser); + verticalLayout->addLayout(contentLayout); + + // init button + auto horizontalLayout = new QHBoxLayout(); + horizontalLayout->setSpacing(6); + + auto pbtnCancel = new DPushButton(this); + pbtnCancel->setText("Cancel"); + pbtnCancel->setObjectName(QStringLiteral("pbtnCancel")); + horizontalLayout->addWidget(pbtnCancel); + + auto pbtnOK = new DSuggestButton(this); + pbtnOK->setText(tr("Start Replay")); + pbtnOK->setObjectName(QStringLiteral("pbtnOK")); + horizontalLayout->addWidget(pbtnOK); + + verticalLayout->addLayout(horizontalLayout); + + QMetaObject::connectSlotsByName(this); +} + +void ReplayDialog::showFileDialog() +{ + QString dir = DFileDialog::getExistingDirectory(this, tr("Open Directory"), + traceDir->text(), + DFileDialog::ShowDirsOnly + | DFileDialog::DontResolveSymlinks); + + if (!dir.isEmpty()) + traceDir->setText(dir); +} diff --git a/src/plugins/debugger/reversedebug/rrdialog.h b/src/plugins/debugger/reversedebug/rrdialog.h new file mode 100644 index 000000000..3695311ea --- /dev/null +++ b/src/plugins/debugger/reversedebug/rrdialog.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later +#ifndef RRDIALOG_H +#define RRDIALOG_H + +#include "services/language/languageservice.h" + +#include +#include + +class RecordDialog : public DTK_WIDGET_NAMESPACE::DDialog +{ + Q_OBJECT + +public: + explicit RecordDialog(QWidget *parent = nullptr); + ~RecordDialog(); + +private slots: + void on_pbtnOK_clicked(); + + void on_pbtnCancel_clicked(); + +signals: + void startRecord(const dpfservice::RunCommandInfo &args); + +private: + void setupUi(); +}; + +class ReplayDialog : public DTK_WIDGET_NAMESPACE::DDialog +{ + Q_OBJECT + +public: + explicit ReplayDialog(QWidget *parent = nullptr); + ~ReplayDialog(); + +signals: + void startReplay(const QString &target); + +private slots: + void on_pbtnOK_clicked(); + + void on_pbtnCancel_clicked(); + + void showFileDialog(); + +private: + void setupUi(); + + Dtk::Widget::DLineEdit *traceDir = nullptr; +}; + +#endif // RRDIALOG_H diff --git a/src/plugins/debugger/texts/debugger_reverse_continue_16px.svg b/src/plugins/debugger/texts/debugger_reverse_continue_16px.svg new file mode 100644 index 000000000..2f39b3dd9 --- /dev/null +++ b/src/plugins/debugger/texts/debugger_reverse_continue_16px.svg @@ -0,0 +1,7 @@ + + + ICON / toolbar /reverse_continue + + + + \ No newline at end of file diff --git a/src/plugins/debugger/texts/debugger_stepback_16px.svg b/src/plugins/debugger/texts/debugger_stepback_16px.svg new file mode 100644 index 000000000..d5e3abc0b --- /dev/null +++ b/src/plugins/debugger/texts/debugger_stepback_16px.svg @@ -0,0 +1,7 @@ + + + ICON / toolbar /stepback + + + + \ No newline at end of file diff --git a/src/plugins/javascript/debugger/jsdebugger.cpp b/src/plugins/javascript/debugger/jsdebugger.cpp index c247ded94..0931ccb53 100644 --- a/src/plugins/javascript/debugger/jsdebugger.cpp +++ b/src/plugins/javascript/debugger/jsdebugger.cpp @@ -89,6 +89,10 @@ void JSDebugger::continueDebug() emit runStateChanged(kRunning); } +void JSDebugger::reverseContinue() +{ +} + void JSDebugger::abortDebug() { } @@ -114,6 +118,16 @@ void JSDebugger::stepOut() runCommand(QScriptEngineDebugger::StepOverAction); } +void JSDebugger::stepBack() +{ + //runCommand(QScriptEngineDebugger::StepOverAction); +} + +bool JSDebugger::supportStepBack() +{ + return false; +} + AbstractDebugger::RunState JSDebugger::getRunState() const { return runState; diff --git a/src/plugins/javascript/debugger/jsdebugger.h b/src/plugins/javascript/debugger/jsdebugger.h index 82db0edea..1cb9ccba2 100644 --- a/src/plugins/javascript/debugger/jsdebugger.h +++ b/src/plugins/javascript/debugger/jsdebugger.h @@ -34,13 +34,16 @@ class JSDebugger : public AbstractDebugger void interruptDebug() override; void continueDebug() override; + void reverseContinue() override; void abortDebug() override; void restartDebug() override; void stepOver() override; void stepIn() override; void stepOut() override; + void stepBack() override; + bool supportStepBack() override; RunState getRunState() const override; bool runCoredump(const QString &target, const QString &core, const QString &kit) override; diff --git a/src/plugins/reversedebug/minidumpruncontrol.cpp b/src/plugins/reversedebug/minidumpruncontrol.cpp index 4dc97f28b..5d5ac962b 100644 --- a/src/plugins/reversedebug/minidumpruncontrol.cpp +++ b/src/plugins/reversedebug/minidumpruncontrol.cpp @@ -151,7 +151,7 @@ void MinidumpRunControl::onStraceExit(int, QProcess::ExitStatus) return; } - QMessageBox::information(nullptr, tr("reverse debug"), tr("Recored done, minidump load ready.")); + QMessageBox::information(nullptr, tr("Event Recorder"), tr("Recored done, minidump load ready.")); } } // namespace Internal diff --git a/src/plugins/reversedebug/reversedebuggermgr.cpp b/src/plugins/reversedebug/reversedebuggermgr.cpp index fb3e6fa81..18dafccce 100644 --- a/src/plugins/reversedebug/reversedebuggermgr.cpp +++ b/src/plugins/reversedebug/reversedebuggermgr.cpp @@ -247,7 +247,7 @@ QString ReverseDebuggerMgr::projectTargetPath() const void ReverseDebuggerMgr::enterReplayEnvironment() { - emit uiController.switchContext(tr("R&everse Debug")); + uiController.switchContext(tr("Ev&ent Recorder")); } static void NumberList2QString(uchar *in, int size, QString &str) diff --git a/src/plugins/reversedebug/reversedebugplugin.cpp b/src/plugins/reversedebug/reversedebugplugin.cpp index 97e2d3914..c37037029 100644 --- a/src/plugins/reversedebug/reversedebugplugin.cpp +++ b/src/plugins/reversedebug/reversedebugplugin.cpp @@ -13,8 +13,8 @@ #include -constexpr char A_REVERSE_DEBUG_RECORD[] = "ReverseDebug.Action.Record"; -constexpr char A_REVERSE_DEBUG_REPLAY[] = "ReverseDebug.Action.Replay"; +constexpr char A_EVENT_RECORDER_RECORD[] = "EventRecorder.Action.Record"; +constexpr char A_EVENT_RECORDER_REPLAY[] = "EventRecorder.Action.Replay"; DWIDGET_USE_NAMESPACE using namespace dpfservice; @@ -34,25 +34,25 @@ bool ReverseDebugPlugin::start() } auto mTools = ActionManager::instance()->actionContainer(M_TOOLS); - auto mReverseDbg = ActionManager::instance()->createContainer(M_TOOLS_REVERSEDEBUG); - mReverseDbg->menu()->setTitle(tr("Reverse debug")); - mTools->addMenu(mReverseDbg); + auto mEventRecorder = ActionManager::instance()->createContainer(M_TOOLS_EVENTRECORDER); + mEventRecorder->menu()->setTitle(tr("Event recorder")); + mTools->addMenu(mEventRecorder); auto actionInit = [&](QAction *action, QString actionID) { auto cmd = ActionManager::instance()->registerAction(action, actionID); - mReverseDbg->addAction(cmd); + mEventRecorder->addAction(cmd); }; - auto recoredAction = new QAction(tr("Record"), mReverseDbg); - actionInit(recoredAction, A_REVERSE_DEBUG_RECORD); - auto replayAction = new QAction(tr("Replay"), mReverseDbg); - actionInit(replayAction, A_REVERSE_DEBUG_REPLAY); + auto recoredAction = new QAction(tr("Record"), mEventRecorder); + actionInit(recoredAction, A_EVENT_RECORDER_RECORD); + auto replayAction = new QAction(tr("Replay"), mEventRecorder); + actionInit(replayAction, A_EVENT_RECORDER_REPLAY); reverseDebug = new ReverseDebuggerMgr(this); connect(recoredAction, &QAction::triggered, reverseDebug, &ReverseDebuggerMgr::recored); connect(replayAction, &QAction::triggered, reverseDebug, &ReverseDebuggerMgr::replay); - windowService->addContextWidget(tr("R&everse Debug"), new AbstractWidget(reverseDebug->getWidget()), false); + windowService->addContextWidget(tr("Ev&ent Recorder"), new AbstractWidget(reverseDebug->getWidget()), false); return true; } diff --git a/src/services/window/windowelement.h b/src/services/window/windowelement.h index 30c40daf9..001aecfce 100644 --- a/src/services/window/windowelement.h +++ b/src/services/window/windowelement.h @@ -61,11 +61,13 @@ inline const QString MWMDA_ATTACH_DEBUG { QAction::tr("Attaching to Running Prog inline const QString MWMDA_RUNNING { QAction::tr("Running") }; inline const QString MWMDA_INTERRUPT { QAction::tr("Interrupt") }; inline const QString MWMDA_CONTINUE { QAction::tr("Continue") }; +inline const QString MWMDA_REVERSE_CONTINUE { QAction::tr("Reverse Continue") }; inline const QString MWMDA_ABORT_DEBUGGING { QAction::tr("Abort Debugging") }; inline const QString MWMDA_RESTART_DEBUGGING { QAction::tr("Restart Debugging") }; inline const QString MWMDA_STEP_OVER { QAction::tr("Step Over") }; inline const QString MWMDA_STEP_IN { QAction::tr("Step In") }; inline const QString MWMDA_STEP_OUT { QAction::tr("Step Out") }; +inline const QString MWMDA_STEP_BACK { QAction::tr("Step Back") }; inline const QString MWMDA_REMOTE_DEBUG { QAction::tr("Remote Debug") }; // MWMTA = MWM Tool Action diff --git a/src/tools/debugadapter/dapproxy.h b/src/tools/debugadapter/dapproxy.h index f6f77d320..0f91f715f 100644 --- a/src/tools/debugadapter/dapproxy.h +++ b/src/tools/debugadapter/dapproxy.h @@ -27,10 +27,12 @@ class DapProxy final: public QObject void sigDetach(); void sigDisconnect(); void sigContinue(); + void sigReverseContinue(); void sigPause(); void sigNext(); void sigStepin(); void sigStepout(); + void sigStepback(); void sigStepover(); void sigBreakInsert(const QString& path); void sigThreads(); diff --git a/src/tools/debugadapter/dapsession.cpp b/src/tools/debugadapter/dapsession.cpp index 99d66270c..f0f080105 100644 --- a/src/tools/debugadapter/dapsession.cpp +++ b/src/tools/debugadapter/dapsession.cpp @@ -227,10 +227,12 @@ void DapSession::initializeDebugMgr() connect(DapProxy::instance(), &DapProxy::sigUpdateBreakpoints, d->debugger, &DebugManager::updateBreakpoints, SequentialExecution); connect(DapProxy::instance(), &DapProxy::sigLaunchLocal, d->debugger, &DebugManager::launchLocal, SequentialExecution); connect(DapProxy::instance(), &DapProxy::sigContinue, d->debugger, &DebugManager::commandContinue, SequentialExecution); + connect(DapProxy::instance(), &DapProxy::sigReverseContinue, d->debugger, &DebugManager::commandReverseContinue, SequentialExecution); connect(DapProxy::instance(), &DapProxy::sigPause, d->debugger, &DebugManager::pauseDebugger, SequentialExecution); connect(DapProxy::instance(), &DapProxy::sigNext, d->debugger, &DebugManager::commandNext, SequentialExecution); connect(DapProxy::instance(), &DapProxy::sigStepin, d->debugger, &DebugManager::commandStep, SequentialExecution); connect(DapProxy::instance(), &DapProxy::sigStepout, d->debugger, &DebugManager::commandFinish, SequentialExecution); + connect(DapProxy::instance(), &DapProxy::sigStepback, d->debugger, &DebugManager::commandBack, SequentialExecution); connect(DapProxy::instance(), &DapProxy::sigThreads, d->debugger, &DebugManager::threadInfo, SequentialExecution); connect(DapProxy::instance(), &DapProxy::sigSelectThread, d->debugger, &DebugManager::threadSelect, SequentialExecution); connect(DapProxy::instance(), &DapProxy::sigStackTrace, d->debugger, &DebugManager::stackListFrames, SequentialExecution); @@ -342,10 +344,13 @@ void DapSession::registerHanlder() QStringList arguments; arguments.push_back(request.program.value().c_str()); if (request.args.has_value()) { - arguments.insert(0, "--args"); - foreach(auto arg, request.args.value()) { + foreach (auto arg, request.args.value()) { arguments.push_back(arg.c_str()); } + // --args : add arguments for gdb. except : rr, runcoredump + if (request.name.value() != "rr" && arguments.size() >= 2 && !(arguments.size() == 2 && arguments.last().endsWith(".core"))) + arguments.insert(0, "--args"); + arguments.removeAll(""); } d->debugger->initDebugger(request.name.value().c_str(), arguments); if (request.environment.has_value()) { @@ -441,6 +446,14 @@ void DapSession::registerHanlder() return dap::ContinueResponse(); }); + d->session->registerHandler([&](const dap::ReverseContinueRequest &request) { + Q_UNUSED(request) + Log("<-- Server received reverse continue request from client\n") + emit DapProxy::instance()->sigReverseContinue(); + Log("--> Server received reverse continue request from client\n") + return dap::ReverseContinueResponse(); + }); + // The Next request instructs the debugger to single line step for a specific // thread. // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Next @@ -478,6 +491,14 @@ void DapSession::registerHanlder() return dap::StepOutResponse(); }); + d->session->registerHandler([&](const dap::StepBackRequest &request) { + Q_UNUSED(request) + Log("<-- Server received stepback request from client\n") + emit DapProxy::instance()->sigStepback(); + Log("--> Server sent stepback response to client\n") + return dap::StepBackResponse(); + }); + // The BreakpointLocations request returns all possible locations for source breakpoints in a range d->session->registerHandler([&](const dap::BreakpointLocationsRequest &request) { Q_UNUSED(request) @@ -790,6 +811,7 @@ InitializeResponse DapSession::handleInitializeReq(const InitializeRequest &requ response.supportsDisassembleRequest = true; response.supportsGotoTargetsRequest = true; response.supportsHitConditionalBreakpoints = true; + response.supportsStepBack = true; Log("--> Server sent initialize response to client\n") return response; diff --git a/src/tools/debugadapter/debugger/debugger.h b/src/tools/debugadapter/debugger/debugger.h index c72338ae7..0b9d7635d 100644 --- a/src/tools/debugadapter/debugger/debugger.h +++ b/src/tools/debugadapter/debugger/debugger.h @@ -29,9 +29,11 @@ class Debugger : public QObject virtual void pause() = 0; virtual QString commandContinue() = 0; + virtual QString commandReverseContinue() = 0; virtual QString commandNext() = 0; virtual QString commandStep() = 0; virtual QString commandFinish() = 0; + virtual QString commandBack() = 0; virtual QString threadInfo() = 0; virtual QString threadSelect(const int threadId) = 0; diff --git a/src/tools/debugadapter/debugger/gdbmi/gdbdebugger.cpp b/src/tools/debugadapter/debugger/gdbmi/gdbdebugger.cpp index 148f933f6..c1379d1f3 100644 --- a/src/tools/debugadapter/debugger/gdbmi/gdbdebugger.cpp +++ b/src/tools/debugadapter/debugger/gdbmi/gdbdebugger.cpp @@ -62,7 +62,7 @@ GDBDebugger::GDBDebugger(QObject *parent) GDBDebugger::~GDBDebugger() { - + delete d; } void GDBDebugger::init() @@ -156,6 +156,11 @@ QString GDBDebugger::commandContinue() return ("-exec-continue"); } +QString GDBDebugger::commandReverseContinue() +{ + return ("reverse-continue"); +} + QString GDBDebugger::commandNext() { return ("-exec-next"); @@ -171,6 +176,11 @@ QString GDBDebugger::commandFinish() return ("-exec-finish"); } +QString GDBDebugger::commandBack() +{ + return ("reverse-next"); +} + QString GDBDebugger::threadInfo() { return ("-thread-info"); @@ -397,7 +407,18 @@ void GDBDebugger::handleOutputRecord(const QString &outputRecord) break; } case gdbmi::Record::RecordType::console: + { + if (record.message.contains("Core was generated by")) { + dap::StoppedEvent stoppedEvent; + stoppedEvent.reason = "step"; + stoppedEvent.threadId = 1; + stoppedEvent.allThreadsStopped = true; + emit asyncStopped(stoppedEvent); + + d->inferiorRunning.store(false); + } break; + } case gdbmi::Record::RecordType::target: { emit streamConsole(gdbmi::escapedText(record.message)); diff --git a/src/tools/debugadapter/debugger/gdbmi/gdbdebugger.h b/src/tools/debugadapter/debugger/gdbmi/gdbdebugger.h index 45142e9b0..1b1a580d8 100644 --- a/src/tools/debugadapter/debugger/gdbmi/gdbdebugger.h +++ b/src/tools/debugadapter/debugger/gdbmi/gdbdebugger.h @@ -35,9 +35,11 @@ class GDBDebugger : public Debugger void pause() override; QString commandContinue() override; + QString commandReverseContinue() override; QString commandNext() override; QString commandStep() override; QString commandFinish() override; + QString commandBack() override; QString stackListFrames() override; QString stackListVariables() override; @@ -128,4 +130,17 @@ public slots: GDBDebuggerPrivate *const d; }; +class RRDebugger : public GDBDebugger +{ + Q_OBJECT +public: + explicit RRDebugger(QObject *parent = nullptr) { } + + QString program() override { return "rr"; } + QStringList preArguments() override + { + return QStringList { "-q", "-i=mi2", "replay" }; + } +}; + #endif // GDBDEBUGGER_H diff --git a/src/tools/debugadapter/debugger/javascript/jsdebugger.cpp b/src/tools/debugadapter/debugger/javascript/jsdebugger.cpp index 6a799035a..20bce4978 100644 --- a/src/tools/debugadapter/debugger/javascript/jsdebugger.cpp +++ b/src/tools/debugadapter/debugger/javascript/jsdebugger.cpp @@ -98,6 +98,11 @@ QString JSDebugger::commandContinue() return ".continue"; } +QString JSDebugger::commandReverseContinue() +{ + RET_EMPTY +} + QString JSDebugger::commandNext() { return ".next"; @@ -113,6 +118,11 @@ QString JSDebugger::commandFinish() RET_EMPTY } +QString JSDebugger::commandBack() +{ + RET_EMPTY +} + QString JSDebugger::stackListFrames() { return ".backtrace"; diff --git a/src/tools/debugadapter/debugger/javascript/jsdebugger.h b/src/tools/debugadapter/debugger/javascript/jsdebugger.h index e7d4dab58..58de1a3f1 100644 --- a/src/tools/debugadapter/debugger/javascript/jsdebugger.h +++ b/src/tools/debugadapter/debugger/javascript/jsdebugger.h @@ -33,9 +33,11 @@ class JSDebugger : public Debugger void pause() override; QString commandContinue() override; + QString commandReverseContinue() override; QString commandNext() override; QString commandStep() override; QString commandFinish() override; + QString commandBack() override; QString stackListFrames() override; QString stackListVariables() override; diff --git a/src/tools/debugadapter/debugmanager.cpp b/src/tools/debugadapter/debugmanager.cpp index 9a4ed6af6..45491288c 100644 --- a/src/tools/debugadapter/debugmanager.cpp +++ b/src/tools/debugadapter/debugmanager.cpp @@ -121,6 +121,8 @@ void DebugManager::initDebugger(const QString &program, const QStringList &argum d->debugger.reset(new GDBDebugger()); } else if (program.contains("jsdbg")) { d->debugger.reset(new JSDebugger()); + } else if (program == "rr") { + d->debugger.reset(new RRDebugger()); } } @@ -307,6 +309,11 @@ void DebugManager::commandContinue() command(d->debugger->commandContinue()); } +void DebugManager::commandReverseContinue() +{ + command(d->debugger->commandReverseContinue()); +} + void DebugManager::commandNext() { command(d->debugger->commandNext()); //step over @@ -322,6 +329,11 @@ void DebugManager::commandFinish() command(d->debugger->commandFinish()); //step out } +void DebugManager::commandBack() +{ + command(d->debugger->commandBack()); //step back +} + void DebugManager::threadSelect(const int threadId) { command(d->debugger->threadSelect(threadId)); diff --git a/src/tools/debugadapter/debugmanager.h b/src/tools/debugadapter/debugmanager.h index fe51b1465..f489883d2 100644 --- a/src/tools/debugadapter/debugmanager.h +++ b/src/tools/debugadapter/debugmanager.h @@ -50,9 +50,11 @@ class DebugManager : public QObject void pauseDebugger(); void commandContinue(); + void commandReverseContinue(); void commandNext(); void commandStep(); void commandFinish(); + void commandBack(); void threadInfo(); void threadSelect(const int threadId);