diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index 3634d132cac..029dd4b87d6 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -90,6 +90,50 @@ signals: void kickoffTerminalProcessRequested(); }; +class EnginesDriver : public QObject +{ + Q_OBJECT + +public: + ~EnginesDriver() { clearEngines(); } + + Result setupEngines(RunControl *runControl, const DebuggerRunParameters &rp); + Result checkBreakpoints() const; + QString debuggerName() const + { + return Utils::transform(m_engines, &DebuggerEngine::objectName).join(" "); + } + QString startParameters() const + { + return m_engines.isEmpty() ? QString() : m_engines.first()->formatStartParameters(); + } + bool isRunning() const { return m_runningEngines > 0; } + void start(); + void stop() + { + Utils::reverseForeach(m_engines, [](DebuggerEngine *engine) { engine->quitDebugger(); }); + } + void showMessage(const QString &msg, int channel = LogDebug, int timeout = -1); + +signals: + void done(DoneResult result); + void interruptTerminalRequested(); + void kickoffTerminalProcessRequested(); + void started(); + +private: + void clearEngines() + { + qDeleteAll(m_engines); + m_engines.clear(); + } + + RunControl *m_runControl = nullptr; + QList> m_engines; + int m_runningEngines = 0; + int m_snapshotCounter = 0; +}; + class DebuggerRunToolPrivate { public: @@ -521,6 +565,137 @@ static int newRunId() return ++toolRunCount; } +Result EnginesDriver::setupEngines(RunControl *runControl, const DebuggerRunParameters &rp) +{ + m_runControl = runControl; + clearEngines(); + const auto engines = createEngines(runControl, rp); + if (!engines) + return Result::Error(engines.error()); + + m_engines = *engines; + const QString runId = QString::number(newRunId()); + for (auto engine : m_engines) { + if (engine != m_engines.first()) + engine->setSecondaryEngine(); + engine->setRunParameters(rp); + engine->setRunId(runId); + for (auto companion : m_engines) { + if (companion != engine) + engine->addCompanionEngine(companion); + } + engine->setDevice(m_runControl->device()); + } + + return Result::Ok; +} + +Result EnginesDriver::checkBreakpoints() const +{ + QStringList unhandledIds; + bool hasQmlBreakpoints = false; + for (const GlobalBreakpoint &gbp : BreakpointManager::globalBreakpoints()) { + if (gbp->isEnabled()) { + const BreakpointParameters &bp = gbp->requestedParameters(); + hasQmlBreakpoints = hasQmlBreakpoints || bp.isQmlFileAndLineBreakpoint(); + const auto engineAcceptsBp = [bp](const DebuggerEngine *engine) { + return engine->acceptsBreakpoint(bp); + }; + if (!Utils::anyOf(m_engines, engineAcceptsBp)) + unhandledIds.append(gbp->displayName()); + } + } + + if (unhandledIds.isEmpty()) + return Result::Ok; + + QString warningMessage = Tr::tr("Some breakpoints cannot be handled by the debugger " + "languages currently active, and will be ignored.

" + "Affected are breakpoints %1").arg(unhandledIds.join(", ")); + if (hasQmlBreakpoints) { + warningMessage += "

" + Tr::tr("QML debugging needs to be enabled both in the Build " + "and the Run settings."); + } + return Result::Error(warningMessage); +} + +void EnginesDriver::start() +{ + const QString runId = QString::number(newRunId()); + for (auto engine : m_engines) { + connect(engine, &DebuggerEngine::interruptTerminalRequested, + this, &EnginesDriver::interruptTerminalRequested); + connect(engine, &DebuggerEngine::kickoffTerminalProcessRequested, + this, &EnginesDriver::kickoffTerminalProcessRequested); + connect(engine, &DebuggerEngine::requestRunControlStop, m_runControl, &RunControl::initiateStop); + + connect(engine, &DebuggerEngine::engineStarted, this, [this, engine] { + ++m_runningEngines; + if (engine->isPrimaryEngine()) { + EngineManager::activateDebugMode(); + emit started(); + } + }); + + connect(engine, &DebuggerEngine::engineFinished, this, [this, engine] { + engine->prepareForRestart(); + if (--m_runningEngines == 0) { + const QString cmd = engine->runParameters().inferior().command.toUserOutput(); + const QString msg = engine->runParameters().exitCode() // Main engine. + ? Tr::tr("Debugging of %1 has finished with exit code %2.") + .arg(cmd) + .arg(*engine->runParameters().exitCode()) + : Tr::tr("Debugging of %1 has finished.").arg(cmd); + m_runControl->postMessage(msg, NormalMessageFormat); + emit done(engine->runParameters().exitCode() ? DoneResult::Error : DoneResult::Success); + } + }); + connect(engine, &DebuggerEngine::postMessageRequested, m_runControl, &RunControl::postMessage); + + if (engine->isPrimaryEngine()) { + connect(engine, &DebuggerEngine::attachToCoreRequested, this, [this](const QString &coreFile) { + auto rc = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE); + rc->copyDataFromRunControl(m_runControl); + rc->resetDataForAttachToCore(); + auto name = QString(Tr::tr("%1 - Snapshot %2").arg(m_runControl->displayName()).arg(++m_snapshotCounter)); + auto debugger = new DebuggerRunTool(rc); + DebuggerRunParameters &rp = debugger->runParameters(); + rp.setStartMode(AttachToCore); + rp.setCloseMode(DetachAtClose); + rp.setDisplayName(name); + rp.setCoreFilePath(FilePath::fromString(coreFile)); + rp.setSnapshot(true); + rc->start(); + }); + } + } + Utils::reverseForeach(m_engines, [](DebuggerEngine *engine) { engine->start(); }); +} + +void EnginesDriver::showMessage(const QString &msg, int channel, int timeout) +{ + if (channel == ConsoleOutput) + debuggerConsole()->printItem(ConsoleItem::DefaultType, msg); + + QTC_ASSERT(!m_engines.isEmpty(), qDebug() << msg; return); + + for (auto engine : m_engines) + engine->showMessage(msg, channel, timeout); + switch (channel) { + case AppOutput: + m_runControl->postMessage(msg, StdOutFormat); + break; + case AppError: + m_runControl->postMessage(msg, StdErrFormat); + break; + case AppStuff: + m_runControl->postMessage(msg, DebugFormat); + break; + default: + break; + } +} + void DebuggerRunTool::continueAfterDebugServerStart() { const auto engines = createEngines(runControl(), runParameters());