From a8a9786b9182b9a2e1b28f81ccaf5845b60a87cc Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 3 Mar 2014 16:59:56 +0100 Subject: [PATCH] Debugger: Add Run-in-Console for LLDB Task-number: QTCREATORBUG-9650 Change-Id: I74586ca8c89efedbb952218497f7acdae500d3b9 Reviewed-by: Eike Ziller --- share/qtcreator/debugger/lldbbridge.py | 6 + src/plugins/debugger/lldb/lldbengine.cpp | 153 +++++++++++++++++++---- src/plugins/debugger/lldb/lldbengine.h | 9 ++ 3 files changed, 147 insertions(+), 21 deletions(-) diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index c8a597724b0..23c37dd88ce 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -660,6 +660,7 @@ class Dumper(DumperBase): self.executable_ = args['executable'] self.startMode_ = args.get('startMode', 1) self.breakOnMain_ = args.get('breakOnMain', 0) + self.useTerminal_ = args.get('useTerminal', 0) self.processArgs_ = args.get('processArgs', []) self.processArgs_ = map(lambda x: self.hexdecode(x), self.processArgs_) self.attachPid_ = args.get('attachPid', 0) @@ -667,6 +668,8 @@ class Dumper(DumperBase): self.remoteChannel_ = args.get('remoteChannel', '') self.platform_ = args.get('platform', '') + self.ignoreStops = 1 if self.useTerminal_ else 0 + if self.platform_: self.debugger.SetCurrentPlatform(self.platform_) # sysroot has to be set *after* the platform @@ -1259,6 +1262,9 @@ class Dumper(DumperBase): if self.isInterrupting_: self.isInterrupting_ = False self.report('state="inferiorstopok"') + elif self.ignoreStops > 0: + self.ignoreStops -= 1 + self.process.Continue() else: self.report('state="stopped"') else: diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 349c9dcc140..b3ffc938508 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -69,6 +69,8 @@ #include #include +using namespace Utils; + namespace Debugger { namespace Internal { @@ -90,6 +92,19 @@ LldbEngine::LldbEngine(const DebuggerStartParameters &startParameters) m_lastToken = 0; setObjectName(QLatin1String("LldbEngine")); + if (startParameters.useTerminal) { + #ifdef Q_OS_WIN + // Windows up to xp needs a workaround for attaching to freshly started processes. see proc_stub_win + if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA) + m_stubProc.setMode(Utils::ConsoleProcess::Suspend); + else + m_stubProc.setMode(Utils::ConsoleProcess::Debug); + #else + m_stubProc.setMode(Utils::ConsoleProcess::Debug); + m_stubProc.setSettings(Core::ICore::settings()); + #endif + } + connect(debuggerCore()->action(AutoDerefPointers), SIGNAL(valueChanged(QVariant)), SLOT(updateLocals())); connect(debuggerCore()->action(CreateFullBacktrace), SIGNAL(triggered()), @@ -103,7 +118,10 @@ LldbEngine::LldbEngine(const DebuggerStartParameters &startParameters) } LldbEngine::~LldbEngine() -{} +{ + m_stubProc.disconnect(); // Avoid spurious state transitions from late exiting stub +} + void LldbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages) { @@ -136,6 +154,8 @@ void LldbEngine::shutdownEngine() { QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state()); m_lldbProc.kill(); + if (startParameters().useTerminal) + m_stubProc.stop(); } void LldbEngine::abortDebugger() @@ -151,13 +171,67 @@ void LldbEngine::abortDebugger() } } +// FIXME: Merge with GdbEngine/QtcProcess +bool LldbEngine::prepareCommand() +{ + if (HostOsInfo::isWindowsHost()) { + DebuggerStartParameters &sp = startParameters(); + QtcProcess::SplitError perr; + sp.processArgs = QtcProcess::prepareArgs(sp.processArgs, &perr, + Utils::HostOsInfo::hostOs(), + &sp.environment, &sp.workingDirectory).toWindowsArgs(); + if (perr != Utils::QtcProcess::SplitOk) { + // perr == BadQuoting is never returned on Windows + // FIXME? QTCREATORBUG-2809 + notifyEngineSetupFailed(); + return false; + } + } + return true; +} + void LldbEngine::setupEngine() { - QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); - if (startParameters().remoteSetupNeeded) - notifyEngineRequestRemoteSetup(); - else - startLldb(); + if (startParameters().useTerminal) { + QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); + showMessage(_("TRYING TO START ADAPTER")); + + // Currently, adapters are not re-used + // // We leave the console open, so recycle it now. + // m_stubProc.blockSignals(true); + // m_stubProc.stop(); + // m_stubProc.blockSignals(false); + + if (!prepareCommand()) { + notifyEngineSetupFailed(); + return; + } + + m_stubProc.setWorkingDirectory(startParameters().workingDirectory); + // Set environment + dumper preload. + m_stubProc.setEnvironment(startParameters().environment); + + connect(&m_stubProc, SIGNAL(processError(QString)), SLOT(stubError(QString))); + connect(&m_stubProc, SIGNAL(processStarted()), SLOT(stubStarted())); + connect(&m_stubProc, SIGNAL(stubStopped()), SLOT(stubExited())); + // FIXME: Starting the stub implies starting the inferior. This is + // fairly unclean as far as the state machine and error reporting go. + + if (!m_stubProc.start(startParameters().executable, + startParameters().processArgs)) { + // Error message for user is delivered via a signal. + //handleAdapterStartFailed(QString()); + notifyEngineSetupFailed(); + return; + } + + } else { + QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); + if (startParameters().remoteSetupNeeded) + notifyEngineRequestRemoteSetup(); + else + startLldb(); + } } void LldbEngine::startLldb() @@ -207,27 +281,44 @@ void LldbEngine::setupInferior() Command cmd("setupInferior"); cmd.arg("executable", executable); - cmd.arg("startMode", sp.startMode); // directly relying on this is brittle wrt. insertions, so check it here cmd.arg("breakOnMain", sp.breakOnMain); + cmd.arg("useTerminal", sp.useTerminal); + cmd.beginList("processArgs"); foreach (const QString &arg, args.toUnixArgs()) cmd.arg(arg.toUtf8().toHex()); cmd.endList(); - // it is better not to check the start mode on the python sid (as we would have to duplicate the - // enum values), and thus we assume that if the sp.attachPID is valid we really have to attach - QTC_CHECK(sp.attachPID <= 0 || (sp.startMode == AttachCrashedExternal - || sp.startMode == AttachExternal)); - cmd.arg("attachPid", sp.attachPID); - cmd.arg("sysRoot", sp.deviceSymbolsRoot.isEmpty() ? sp.sysRoot : sp.deviceSymbolsRoot); - cmd.arg("remoteChannel", ((sp.startMode == AttachToRemoteProcess - || sp.startMode == AttachToRemoteServer) - ? sp.remoteChannel : QString())); - cmd.arg("platform", sp.platform); - QTC_CHECK(!sp.continueAfterAttach || (sp.startMode == AttachToRemoteProcess - || sp.startMode == AttachExternal - || sp.startMode == AttachToRemoteServer)); - m_continueAtNextSpontaneousStop = false; + if (sp.useTerminal) { + QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); + const qint64 attachedPID = m_stubProc.applicationPID(); + const qint64 attachedMainThreadID = m_stubProc.applicationMainThreadID(); + const QString msg = (attachedMainThreadID != -1) + ? QString::fromLatin1("Attaching to %1 (%2)").arg(attachedPID).arg(attachedMainThreadID) + : QString::fromLatin1("Attaching to %1").arg(attachedPID); + showMessage(msg, LogMisc); + cmd.arg("startMode", AttachExternal); + cmd.arg("attachPid", attachedPID); + + } else { + + cmd.arg("startMode", sp.startMode); + // it is better not to check the start mode on the python sid (as we would have to duplicate the + // enum values), and thus we assume that if the sp.attachPID is valid we really have to attach + QTC_CHECK(sp.attachPID <= 0 || (sp.startMode == AttachCrashedExternal + || sp.startMode == AttachExternal)); + cmd.arg("attachPid", sp.attachPID); + cmd.arg("sysRoot", sp.deviceSymbolsRoot.isEmpty() ? sp.sysRoot : sp.deviceSymbolsRoot); + cmd.arg("remoteChannel", ((sp.startMode == AttachToRemoteProcess + || sp.startMode == AttachToRemoteServer) + ? sp.remoteChannel : QString())); + cmd.arg("platform", sp.platform); + QTC_CHECK(!sp.continueAfterAttach || (sp.startMode == AttachToRemoteProcess + || sp.startMode == AttachExternal + || sp.startMode == AttachToRemoteServer)); + m_continueAtNextSpontaneousStop = false; + } + runCommand(cmd); updateLocals(); // update display options } @@ -1356,5 +1447,25 @@ void LldbEngine::Command::endGroup() const args += "},"; } +void LldbEngine::stubStarted() +{ + startLldb(); +} + +void LldbEngine::stubError(const QString &msg) +{ + showMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg); +} + +void LldbEngine::stubExited() +{ + if (state() == EngineShutdownRequested || state() == DebuggerFinished) { + showMessage(_("STUB EXITED EXPECTEDLY")); + return; + } + showMessage(_("STUB EXITED")); + notifyEngineIll(); +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index 4e034984006..7b1d4aca943 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -35,6 +35,8 @@ #include #include +#include + #include #include #include @@ -211,6 +213,13 @@ private: QScopedPointer m_toolTipContext; void showToolTip(); + + // Console handling. + Q_SLOT void stubError(const QString &msg); + Q_SLOT void stubExited(); + Q_SLOT void stubStarted(); + bool prepareCommand(); + Utils::ConsoleProcess m_stubProc; }; } // namespace Internal