Debugger: Use EnginesDriver

Enclose everything inside the DebuggerRunTool's recipe.

Change-Id: I6b182ab7a2fd8d029df7ad719f92bc62caf5c4cb
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2025-03-11 13:48:47 +01:00
parent ac8c15fde8
commit 6d9575f942
2 changed files with 107 additions and 203 deletions

View File

@@ -81,15 +81,6 @@ static QString noDebuggerInKitMessage()
return Tr::tr("The kit does not have a debugger set."); return Tr::tr("The kit does not have a debugger set.");
} }
class GlueInterface : public QObject
{
Q_OBJECT
signals:
void interruptTerminalRequested();
void kickoffTerminalProcessRequested();
};
class EnginesDriver : public QObject class EnginesDriver : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -139,16 +130,13 @@ class DebuggerRunToolPrivate
public: public:
DebuggerRunTool *q = nullptr; DebuggerRunTool *q = nullptr;
void showMessage(const QString &msg, int channel = LogDebug, int timeout = -1);
ExecutableItem coreFileRecipe(); ExecutableItem coreFileRecipe();
ExecutableItem terminalRecipe(const SingleBarrier &barrier); ExecutableItem terminalRecipe(const SingleBarrier &barrier,
const Storage<EnginesDriver> &driverStorage);
ExecutableItem fixupParamsRecipe(); ExecutableItem fixupParamsRecipe();
ExecutableItem debugServerRecipe(const SingleBarrier &barrier); ExecutableItem debugServerRecipe(const SingleBarrier &barrier);
ExecutableItem startEnginesRecipe(const Storage<EnginesDriver> &driverStorage);
int snapshotCounter = 0; ExecutableItem finishEnginesRecipe(const Storage<EnginesDriver> &driverStorage);
// int engineStartsNeeded = 0;
int engineStopsNeeded = 0;
DebuggerRunParameters m_runParameters; DebuggerRunParameters m_runParameters;
@@ -156,36 +144,11 @@ public:
FilePath m_tempCoreFilePath; // TODO: Enclose in the recipe storage when all tasks are there. FilePath m_tempCoreFilePath; // TODO: Enclose in the recipe storage when all tasks are there.
// TaskTree // TaskTree
std::unique_ptr<GlueInterface> m_glue; // Enclose in the recipe storage when all tasks are there.
Tasking::TaskTreeRunner m_taskTreeRunner; Tasking::TaskTreeRunner m_taskTreeRunner;
}; };
} // namespace Internal } // namespace Internal
void DebuggerRunTool::start()
{
d->m_glue.reset(new GlueInterface);
const auto terminalKicker = [this](const SingleBarrier &barrier) {
return d->terminalRecipe(barrier);
};
const auto debugServerKicker = [this](const SingleBarrier &barrier) {
return d->debugServerRecipe(barrier);
};
const Group recipe {
d->coreFileRecipe(),
When (terminalKicker) >> Do {
d->fixupParamsRecipe(),
When (debugServerKicker) >> Do {
Sync([this] { continueAfterDebugServerStart(); })
}
}
};
d->m_taskTreeRunner.start(recipe);
}
ExecutableItem DebuggerRunToolPrivate::coreFileRecipe() ExecutableItem DebuggerRunToolPrivate::coreFileRecipe()
{ {
const FilePath coreFile = m_runParameters.coreFile(); const FilePath coreFile = m_runParameters.coreFile();
@@ -233,7 +196,8 @@ ExecutableItem DebuggerRunToolPrivate::coreFileRecipe()
}; };
} }
ExecutableItem DebuggerRunToolPrivate::terminalRecipe(const SingleBarrier &barrier) ExecutableItem DebuggerRunToolPrivate::terminalRecipe(const SingleBarrier &barrier,
const Storage<EnginesDriver> &driverStorage)
{ {
const auto useTerminal = [this] { const auto useTerminal = [this] {
const bool useCdbConsole = m_runParameters.cppEngineType() == CdbEngineType const bool useCdbConsole = m_runParameters.cppEngineType() == CdbEngineType
@@ -245,7 +209,7 @@ ExecutableItem DebuggerRunToolPrivate::terminalRecipe(const SingleBarrier &barri
return m_runParameters.useTerminal(); return m_runParameters.useTerminal();
}; };
const auto onSetup = [this, barrier](Process &process) { const auto onSetup = [this, barrier, driverStorage](Process &process) {
ProcessRunData stub = m_runParameters.inferior(); ProcessRunData stub = m_runParameters.inferior();
if (m_runParameters.runAsRoot()) { if (m_runParameters.runAsRoot()) {
process.setRunAsRoot(true); process.setRunAsRoot(true);
@@ -261,9 +225,10 @@ ExecutableItem DebuggerRunToolPrivate::terminalRecipe(const SingleBarrier &barri
barrier->advance(); barrier->advance();
}); });
QObject::connect(m_glue.get(), &GlueInterface::interruptTerminalRequested, EnginesDriver *driver = driverStorage.activeStorage();
QObject::connect(driver, &EnginesDriver::interruptTerminalRequested,
&process, &Process::interrupt); &process, &Process::interrupt);
QObject::connect(m_glue.get(), &GlueInterface::kickoffTerminalProcessRequested, QObject::connect(driver, &EnginesDriver::kickoffTerminalProcessRequested,
&process, &Process::kickoffProcess); &process, &Process::kickoffProcess);
}; };
const auto onDone = [this](const Process &process, DoneWith result) { const auto onDone = [this](const Process &process, DoneWith result) {
@@ -504,6 +469,63 @@ ExecutableItem DebuggerRunToolPrivate::debugServerRecipe(const SingleBarrier &ba
}; };
} }
static ExecutableItem doneAwaiter(const Storage<EnginesDriver> &driverStorage)
{
return BarrierTask([driverStorage](Barrier &barrier) {
QObject::connect(driverStorage.activeStorage(), &EnginesDriver::done, &barrier, &Barrier::stopWithResult,
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
});
}
ExecutableItem DebuggerRunToolPrivate::startEnginesRecipe(const Storage<EnginesDriver> &driverStorage)
{
const auto setupEngines = [this, driverStorage] {
EnginesDriver *driver = driverStorage.activeStorage();
RunControl *rc = q->runControl();
if (Result res = driver->setupEngines(rc, m_runParameters); !res) {
q->reportFailure(res.error());
return false;
}
if (Result res = driver->checkBreakpoints(); !res) {
driver->showMessage(res.error(), LogWarning);
if (settings().showUnsupportedBreakpointWarning()) {
bool doNotAskAgain = false;
CheckableDecider decider(&doNotAskAgain);
CheckableMessageBox::information(Tr::tr("Debugger"), res.error(), decider, QMessageBox::Ok);
if (doNotAskAgain) {
settings().showUnsupportedBreakpointWarning.setValue(false);
settings().showUnsupportedBreakpointWarning.writeSettings();
}
}
}
rc->postMessage(Tr::tr("Debugging %1 ...").arg(m_runParameters.inferior().command.toUserOutput()),
NormalMessageFormat);
const QString message = Tr::tr("Starting debugger \"%1\" for ABI \"%2\"...")
.arg(driver->debuggerName(), m_runParameters.toolChainAbi().toString());
DebuggerMainWindow::showStatusMessage(message, 10000);
driver->showMessage(driver->startParameters(), LogDebug);
driver->showMessage(DebuggerSettings::dump(), LogDebug);
driver->start();
QObject::connect(driver, &EnginesDriver::started, q, &RunWorker::reportStarted);
return true;
};
return If (setupEngines) >> Then {
doneAwaiter(driverStorage)
};
}
ExecutableItem DebuggerRunToolPrivate::finishEnginesRecipe(const Storage<EnginesDriver> &driverStorage)
{
const auto isRunning = [driverStorage] { return driverStorage->isRunning(); };
return If (isRunning) >> Then {
Sync([driverStorage] { driverStorage->stop(); }),
doneAwaiter(driverStorage)
};
}
static expected_str<QList<QPointer<Internal::DebuggerEngine>>> createEngines( static expected_str<QList<QPointer<Internal::DebuggerEngine>>> createEngines(
RunControl *runControl, const DebuggerRunParameters &rp) RunControl *runControl, const DebuggerRunParameters &rp)
{ {
@@ -696,144 +718,52 @@ void EnginesDriver::showMessage(const QString &msg, int channel, int timeout)
} }
} }
void DebuggerRunTool::continueAfterDebugServerStart() void DebuggerRunTool::start()
{ {
const auto engines = createEngines(runControl(), runParameters()); const Storage<EnginesDriver> driverStorage;
if (!engines) {
reportFailure(engines.error());
return;
}
m_engines = *engines;
const QString runId = QString::number(newRunId()); const auto onSetup = [this] {
for (auto engine : m_engines) { connect(this, &DebuggerRunTool::canceled, runStorage().activeStorage(), &RunInterface::canceled);
if (engine != m_engines.first()) };
engine->setSecondaryEngine();
engine->setRunParameters(d->m_runParameters);
engine->setRunId(runId);
for (auto companion : m_engines) {
if (companion != engine)
engine->addCompanionEngine(companion);
}
connect(engine, &DebuggerEngine::interruptTerminalRequested,
d->m_glue.get(), &GlueInterface::interruptTerminalRequested);
connect(engine, &DebuggerEngine::kickoffTerminalProcessRequested,
d->m_glue.get(), &GlueInterface::kickoffTerminalProcessRequested);
engine->setDevice(runControl()->device());
auto rc = runControl();
connect(engine, &DebuggerEngine::requestRunControlStop, rc, &RunControl::initiateStop);
connect(engine, &DebuggerEngine::engineStarted, this, [this, engine] { const auto terminalKicker = [this, driverStorage](const SingleBarrier &barrier) {
++d->engineStopsNeeded; return d->terminalRecipe(barrier, driverStorage);
// Correct: };
// if (--d->engineStartsNeeded == 0) {
// EngineManager::activateDebugMode();
// reportStarted();
// }
// Feels better, as the QML Engine might attach late or not at all. const auto debugServerKicker = [this](const SingleBarrier &barrier) {
if (engine->isPrimaryEngine()) { return d->debugServerRecipe(barrier);
EngineManager::activateDebugMode(); };
reportStarted();
}
});
connect(engine, &DebuggerEngine::engineFinished, this, [this, engine] { const Group recipe {
engine->prepareForRestart(); runStorage(),
if (--d->engineStopsNeeded == 0) { driverStorage,
const QString cmd = d->m_runParameters.inferior().command.toUserOutput(); continueOnError,
const QString msg = engine->runParameters().exitCode() // Main engine. onGroupSetup(onSetup),
? Tr::tr("Debugging of %1 has finished with exit code %2.") Group {
.arg(cmd) d->coreFileRecipe(),
.arg(*engine->runParameters().exitCode()) When (terminalKicker) >> Do {
: Tr::tr("Debugging of %1 has finished.").arg(cmd); d->fixupParamsRecipe(),
appendMessage(msg, NormalMessageFormat); When (debugServerKicker) >> Do {
reportStopped(); d->startEnginesRecipe(driverStorage)
}
});
connect(engine, &DebuggerEngine::postMessageRequested, rc, &RunControl::postMessage);
// ++d->engineStartsNeeded;
if (engine->isPrimaryEngine()) {
connect(engine, &DebuggerEngine::attachToCoreRequested, this, [this](const QString &coreFile) {
auto rc = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
rc->copyDataFromRunControl(runControl());
rc->resetDataForAttachToCore();
auto name = QString(Tr::tr("%1 - Snapshot %2").arg(runControl()->displayName()).arg(++d->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();
});
}
}
if (d->m_runParameters.startMode() != AttachToCore) {
QStringList unhandledIds;
bool hasQmlBreakpoints = false;
for (const GlobalBreakpoint &gbp : BreakpointManager::globalBreakpoints()) {
if (gbp->isEnabled()) {
const BreakpointParameters &bp = gbp->requestedParameters();
hasQmlBreakpoints = hasQmlBreakpoints || bp.isQmlFileAndLineBreakpoint();
auto engineAcceptsBp = [bp](const DebuggerEngine *engine) {
return engine->acceptsBreakpoint(bp);
};
if (!Utils::anyOf(m_engines, engineAcceptsBp))
unhandledIds.append(gbp->displayName());
}
}
if (!unhandledIds.isEmpty()) {
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.");
}
d->showMessage(warningMessage, LogWarning);
if (settings().showUnsupportedBreakpointWarning()) {
bool doNotAskAgain = false;
CheckableDecider decider(&doNotAskAgain);
CheckableMessageBox::information(
Tr::tr("Debugger"),
warningMessage,
decider,
QMessageBox::Ok);
if (doNotAskAgain) {
settings().showUnsupportedBreakpointWarning.setValue(false);
settings().showUnsupportedBreakpointWarning.writeSettings();
} }
} }
} }.withCancel(canceler()),
} d->finishEnginesRecipe(driverStorage)
};
appendMessage(Tr::tr("Debugging %1 ...").arg(d->m_runParameters.inferior().command.toUserOutput()), d->m_taskTreeRunner.start(recipe, {}, [this](DoneWith result) {
NormalMessageFormat); if (result == DoneWith::Success)
const QString debuggerName = Utils::transform<QStringList>(m_engines, &DebuggerEngine::objectName).join(" "); reportStopped();
else
const QString message = Tr::tr("Starting debugger \"%1\" for ABI \"%2\"...") reportFailure();
.arg(debuggerName).arg(d->m_runParameters.toolChainAbi().toString()); });
DebuggerMainWindow::showStatusMessage(message, 10000);
d->showMessage(m_engines.first()->formatStartParameters(), LogDebug);
d->showMessage(DebuggerSettings::dump(), LogDebug);
Utils::reverseForeach(m_engines, [](DebuggerEngine *engine) { engine->start(); });
} }
void DebuggerRunTool::stop() void DebuggerRunTool::stop()
{ {
QTC_ASSERT(!m_engines.isEmpty(), reportStopped(); return); if (!d->m_taskTreeRunner.isRunning())
Utils::reverseForeach(m_engines, [](DebuggerEngine *engine) { engine->quitDebugger(); }); return;
emit canceled();
} }
void DebuggerRunTool::setupPortsGatherer() void DebuggerRunTool::setupPortsGatherer()
@@ -878,36 +808,9 @@ DebuggerRunTool::~DebuggerRunTool()
if (d->m_runParameters.isSnapshot() && !d->m_runParameters.coreFile().isEmpty()) if (d->m_runParameters.isSnapshot() && !d->m_runParameters.coreFile().isEmpty())
d->m_runParameters.coreFile().removeFile(); d->m_runParameters.coreFile().removeFile();
qDeleteAll(m_engines);
m_engines.clear();
delete d; delete d;
} }
void DebuggerRunToolPrivate::showMessage(const QString &msg, int channel, int timeout)
{
if (channel == ConsoleOutput)
debuggerConsole()->printItem(ConsoleItem::DefaultType, msg);
QTC_ASSERT(!q->m_engines.isEmpty(), qDebug() << msg; return);
for (auto engine : q->m_engines)
engine->showMessage(msg, channel, timeout);
switch (channel) {
case AppOutput:
q->appendMessage(msg, StdOutFormat);
break;
case AppError:
q->appendMessage(msg, StdErrFormat);
break;
case AppStuff:
q->appendMessage(msg, DebugFormat);
break;
default:
break;
}
}
class DebuggerRunWorkerFactory final : public ProjectExplorer::RunWorkerFactory class DebuggerRunWorkerFactory final : public ProjectExplorer::RunWorkerFactory
{ {
public: public:

View File

@@ -16,6 +16,8 @@ namespace Internal { class DebuggerRunToolPrivate; }
class DEBUGGER_EXPORT DebuggerRunTool final : public ProjectExplorer::RunWorker class DEBUGGER_EXPORT DebuggerRunTool final : public ProjectExplorer::RunWorker
{ {
Q_OBJECT
public: public:
explicit DebuggerRunTool(ProjectExplorer::RunControl *runControl); explicit DebuggerRunTool(ProjectExplorer::RunControl *runControl);
~DebuggerRunTool() override; ~DebuggerRunTool() override;
@@ -27,12 +29,11 @@ public:
DebuggerRunParameters &runParameters(); DebuggerRunParameters &runParameters();
private: signals:
void continueAfterDebugServerStart(); void canceled();
friend class Internal::DebuggerRunToolPrivate; private:
Internal::DebuggerRunToolPrivate *d; Internal::DebuggerRunToolPrivate *d;
QList<QPointer<Internal::DebuggerEngine>> m_engines;
}; };
void setupDebuggerRunWorker(); void setupDebuggerRunWorker();