Debugger: Introduce EnginesDriver

Change-Id: I53635ea6e18d75eee7a41f958009a01708d8ec89
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2025-03-10 13:35:51 +01:00
parent c0a4077f61
commit ac8c15fde8

View File

@@ -90,6 +90,50 @@ signals:
void kickoffTerminalProcessRequested(); 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<QStringList>(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<QPointer<Internal::DebuggerEngine>> m_engines;
int m_runningEngines = 0;
int m_snapshotCounter = 0;
};
class DebuggerRunToolPrivate class DebuggerRunToolPrivate
{ {
public: public:
@@ -521,6 +565,137 @@ static int newRunId()
return ++toolRunCount; 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.<p>"
"Affected are breakpoints %1").arg(unhandledIds.join(", "));
if (hasQmlBreakpoints) {
warningMessage += "<p>" + 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() void DebuggerRunTool::continueAfterDebugServerStart()
{ {
const auto engines = createEngines(runControl(), runParameters()); const auto engines = createEngines(runControl(), runParameters());