diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index b50e1ceadbe..02e6491128b 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -362,7 +362,7 @@ void CdbDebugEngine::startDebugger(const QSharedPointer } switch (sp->startMode) { case AttachCore: - case StartRemote: + case AttachToRemote: warning(QLatin1String("Internal error: Mode not supported.")); setState(AdapterStartFailed, Q_FUNC_INFO, __LINE__); emit startFailed(); diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h index eb6786d9c82..1aada273ac6 100644 --- a/src/plugins/debugger/debuggerconstants.h +++ b/src/plugins/debugger/debuggerconstants.h @@ -114,7 +114,8 @@ enum DebuggerStartMode AttachCrashedExternal, // Attach to crashed process by process id AttachTcf, // Attach to a running Target Communication Framework agent AttachCore, // Attach to a core file - StartRemote // Start and attach to a remote process + AttachToRemote, // Start and attach to a remote process + StartRemoteGdb // Start gdb itself remotely }; enum DebuggerCapabilities diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp index 9b09cd09b4d..b2d36d0e4e0 100644 --- a/src/plugins/debugger/debuggermanager.cpp +++ b/src/plugins/debugger/debuggermanager.cpp @@ -1097,7 +1097,7 @@ void DebuggerManager::startNewDebugger(const DebuggerStartParametersPtr &sp) d->m_engine = debuggerEngineForToolChain(sp->toolChainType); if (d->m_engine == 0 - && startMode != StartRemote + && startMode != AttachToRemote && !sp->executable.isEmpty()) d->m_engine = debuggerEngineForExecutable( sp->executable, &errorMessage, &settingsIdHint); diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h index 6ba60846420..19991c84ed9 100644 --- a/src/plugins/debugger/debuggermanager.h +++ b/src/plugins/debugger/debuggermanager.h @@ -33,6 +33,8 @@ #include "debugger_global.h" #include "debuggerconstants.h" +#include + #include #include #include @@ -122,11 +124,12 @@ public: QString sysRoot; QString debuggerCommand; int toolChainType; - QString remoteDumperLib; + QByteArray remoteDumperLib; QString qtInstallPath; QString dumperLibrary; QStringList dumperLibraryLocations; + Core::SshServerInfo sshserver; DebuggerStartMode startMode; }; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 70541e03d69..0418a1830e1 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -1509,7 +1509,7 @@ void DebuggerPlugin::startRemoteApplication() sp->debuggerCommand = dlg.debugger(); // Override toolchain-detection. if (!sp->debuggerCommand.isEmpty()) sp->toolChainType = ProjectExplorer::ToolChain::INVALID; - sp->startMode = StartRemote; + sp->startMode = AttachToRemote; if (dlg.useServerStartScript()) sp->serverStartScript = dlg.serverStartScript(); sp->sysRoot = dlg.sysRoot(); diff --git a/src/plugins/debugger/gdb/abstractgdbadapter.cpp b/src/plugins/debugger/gdb/abstractgdbadapter.cpp index ea154722be0..9ec60a8a9c2 100644 --- a/src/plugins/debugger/gdb/abstractgdbadapter.cpp +++ b/src/plugins/debugger/gdb/abstractgdbadapter.cpp @@ -29,6 +29,8 @@ #include "abstractgdbadapter.h" +#include "abstractgdbprocess.h" + #include #include @@ -61,7 +63,7 @@ const char *AbstractGdbAdapter::inferiorShutdownCommand() const void AbstractGdbAdapter::write(const QByteArray &data) { - m_engine->m_gdbProc.write(data); + gdbProc()->write(data); } bool AbstractGdbAdapter::isTrkAdapter() const diff --git a/src/plugins/debugger/gdb/abstractgdbadapter.h b/src/plugins/debugger/gdb/abstractgdbadapter.h index 69ad84094ce..3d36f2453ba 100644 --- a/src/plugins/debugger/gdb/abstractgdbadapter.h +++ b/src/plugins/debugger/gdb/abstractgdbadapter.h @@ -38,6 +38,8 @@ namespace Debugger { namespace Internal { +class AbstractGdbProcess; + // AbstractGdbAdapter is inherited by PlainGdbAdapter used for local // debugging and TrkGdbAdapter used for on-device debugging. // In the PlainGdbAdapter case it's just a wrapper around a QProcess running @@ -64,6 +66,7 @@ public: virtual void interruptInferior() = 0; virtual void shutdown(); virtual const char *inferiorShutdownCommand() const; + virtual AbstractGdbProcess *gdbProc() = 0; virtual DumperHandling dumperHandling() const = 0; diff --git a/src/plugins/debugger/gdb/abstractgdbprocess.cpp b/src/plugins/debugger/gdb/abstractgdbprocess.cpp new file mode 100644 index 00000000000..55c6b676577 --- /dev/null +++ b/src/plugins/debugger/gdb/abstractgdbprocess.cpp @@ -0,0 +1,36 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "abstractgdbprocess.h" + +namespace Debugger { +namespace Internal { + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/gdb/abstractgdbprocess.h b/src/plugins/debugger/gdb/abstractgdbprocess.h new file mode 100644 index 00000000000..61fa160abbc --- /dev/null +++ b/src/plugins/debugger/gdb/abstractgdbprocess.h @@ -0,0 +1,76 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef GDBPROCESSWRAPPER_H +#define GDBPROCESSWRAPPER_H + +#include +#include + +namespace Debugger { +namespace Internal { + +class AbstractGdbProcess : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(AbstractGdbProcess) +public: + virtual QByteArray readAllStandardOutput() = 0; + virtual QByteArray readAllStandardError() = 0; + + virtual void start(const QString &cmd, const QStringList &args) = 0; + virtual bool waitForStarted() = 0; + virtual qint64 write(const QByteArray &data) = 0; + virtual void kill() = 0; + + virtual QProcess::ProcessState state() const = 0; + virtual QString errorString() const = 0; + + virtual QProcessEnvironment processEnvironment() const = 0; + virtual void setProcessEnvironment(const QProcessEnvironment &env) = 0; + virtual void setEnvironment(const QStringList &env) = 0; + virtual void setWorkingDirectory(const QString &dir) = 0; + + virtual ~AbstractGdbProcess() {} + +signals: + void error(QProcess::ProcessError); + void finished(int exitCode, QProcess::ExitStatus exitStatus); + void readyReadStandardError(); + void readyReadStandardOutput(); + +protected: + explicit AbstractGdbProcess(QObject *parent = 0) : QObject(parent) {} + +}; + +} // namespace Internal +} // namespace Debugger + +#endif // GDBPROCESSWRAPPER_H diff --git a/src/plugins/debugger/gdb/plaingdbadapter.cpp b/src/plugins/debugger/gdb/abstractplaingdbadapter.cpp similarity index 54% rename from src/plugins/debugger/gdb/plaingdbadapter.cpp rename to src/plugins/debugger/gdb/abstractplaingdbadapter.cpp index 05c6b426471..7426a8156fc 100644 --- a/src/plugins/debugger/gdb/plaingdbadapter.cpp +++ b/src/plugins/debugger/gdb/abstractplaingdbadapter.cpp @@ -27,104 +27,53 @@ ** **************************************************************************/ -#include "plaingdbadapter.h" +#include "abstractplaingdbadapter.h" -#include "gdbengine.h" -#include "procinterrupt.h" -#include "debuggerstringutils.h" #include "debuggeractions.h" +#include "debuggerstringutils.h" #include -#include - namespace Debugger { namespace Internal { #define CB(callback) \ - static_cast(&PlainGdbAdapter::callback), \ + static_cast(&AbstractPlainGdbAdapter::callback), \ STRINGIFY(callback) -/////////////////////////////////////////////////////////////////////// -// -// PlainGdbAdapter -// -/////////////////////////////////////////////////////////////////////// - -PlainGdbAdapter::PlainGdbAdapter(GdbEngine *engine, QObject *parent) +AbstractPlainGdbAdapter::AbstractPlainGdbAdapter(GdbEngine *engine, + QObject *parent) : AbstractGdbAdapter(engine, parent) { - // Output - connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)), - engine, SLOT(readDebugeeOutput(QByteArray))); } -AbstractGdbAdapter::DumperHandling PlainGdbAdapter::dumperHandling() const -{ - // LD_PRELOAD fails for System-Qt on Mac. -#if defined(Q_OS_WIN) || defined(Q_OS_MAC) - return DumperLoadedByGdb; -#else - return DumperLoadedByGdbPreload; -#endif -} -void PlainGdbAdapter::startAdapter() -{ - QTC_ASSERT(state() == EngineStarting, qDebug() << state()); - setState(AdapterStarting); - debugMessage(_("TRYING TO START ADAPTER")); - - QStringList gdbArgs; - - if (!m_outputCollector.listen()) { - emit adapterStartFailed(tr("Cannot set up communication with child process: %1") - .arg(m_outputCollector.errorString()), QString()); - return; - } - gdbArgs.append(_("--tty=") + m_outputCollector.serverName()); - - if (!startParameters().workingDir.isEmpty()) - m_engine->m_gdbProc.setWorkingDirectory(startParameters().workingDir); - if (!startParameters().environment.isEmpty()) - m_engine->m_gdbProc.setEnvironment(startParameters().environment); - - if (!m_engine->startGdb(gdbArgs)) { - m_outputCollector.shutdown(); - return; - } - - emit adapterStarted(); -} - -void PlainGdbAdapter::startInferior() +void AbstractPlainGdbAdapter::startInferior() { QTC_ASSERT(state() == InferiorStarting, qDebug() << state()); if (!startParameters().processArgs.isEmpty()) { QString args = startParameters().processArgs.join(_(" ")); - m_engine->postCommand("-exec-arguments " + args.toLocal8Bit()); + m_engine->postCommand("-exec-arguments " + toLocalEncoding(args)); } - QFileInfo fi(startParameters().executable); - QByteArray path = fi.absoluteFilePath().toLocal8Bit(); - m_engine->postCommand("-file-exec-and-symbols \"" + path + '"', + m_engine->postCommand("-file-exec-and-symbols \"" + execFilePath() + '"', CB(handleFileExecAndSymbols)); } -void PlainGdbAdapter::handleFileExecAndSymbols(const GdbResponse &response) +void AbstractPlainGdbAdapter::handleFileExecAndSymbols(const GdbResponse &response) { QTC_ASSERT(state() == InferiorStarting, qDebug() << state()); if (response.resultClass == GdbResultDone) { -#ifdef Q_OS_LINUX - // Old gdbs do not announce the PID for programs without pthreads. - // Note that successfully preloading the debugging helpers will - // automatically load pthreads, so this will be unnecessary. - if (m_engine->m_gdbVersion < 70000) - m_engine->postCommand("info target", CB(handleInfoTarget)); -#endif + if (infoTargetNecessary()) { + // Old gdbs do not announce the PID for programs without pthreads. + // Note that successfully preloading the debugging helpers will + // automatically load pthreads, so this will be unnecessary. + if (m_engine->m_gdbVersion < 70000) + m_engine->postCommand("info target", CB(handleInfoTarget)); + } emit inferiorPrepared(); } else { QByteArray ba = response.data.findChild("msg").data(); - QString msg = QString::fromLocal8Bit(ba); + QString msg = fromLocalEncoding(ba); // Extend the message a bit in unknown cases. if (!ba.endsWith("File format not recognized")) msg = tr("Starting executable failed:\n") + msg; @@ -132,8 +81,31 @@ void PlainGdbAdapter::handleFileExecAndSymbols(const GdbResponse &response) } } -#ifdef Q_OS_LINUX -void PlainGdbAdapter::handleInfoTarget(const GdbResponse &response) +void AbstractPlainGdbAdapter::startInferiorPhase2() +{ + setState(InferiorRunningRequested); + m_engine->postCommand("-exec-run", GdbEngine::RunRequest, CB(handleExecRun)); +} + +void AbstractPlainGdbAdapter::handleExecRun(const GdbResponse &response) +{ + if (response.resultClass == GdbResultRunning) { + QTC_ASSERT(state() == InferiorRunning, qDebug() << state()); + debugMessage(_("INFERIOR STARTED")); + showStatusMessage(msgInferiorStarted()); + // FIXME: That's the wrong place for it. + if (theDebuggerBoolSetting(EnableReverseDebugging)) + m_engine->postCommand("target record"); + } else { + QTC_ASSERT(state() == InferiorRunningRequested, qDebug() << state()); + QString msg = fromLocalEncoding(response.data.findChild("msg").data()); + //QTC_ASSERT(status() == InferiorRunning, /**/); + //interruptInferior(); + emit inferiorStartFailed(msg); + } +} + +void AbstractPlainGdbAdapter::handleInfoTarget(const GdbResponse &response) { if (response.resultClass == GdbResultDone) { // [some leading stdout here] @@ -153,49 +125,6 @@ void PlainGdbAdapter::handleInfoTarget(const GdbResponse &response) emit inferiorStartFailed(_("Fetching start address failed")); } } -#endif -void PlainGdbAdapter::startInferiorPhase2() -{ - setState(InferiorRunningRequested); - m_engine->postCommand("-exec-run", GdbEngine::RunRequest, CB(handleExecRun)); -} - -void PlainGdbAdapter::handleExecRun(const GdbResponse &response) -{ - if (response.resultClass == GdbResultRunning) { - QTC_ASSERT(state() == InferiorRunning, qDebug() << state()); - debugMessage(_("INFERIOR STARTED")); - showStatusMessage(msgInferiorStarted()); - // FIXME: That's the wrong place for it. - if (theDebuggerBoolSetting(EnableReverseDebugging)) - m_engine->postCommand("target record"); - } else { - QTC_ASSERT(state() == InferiorRunningRequested, qDebug() << state()); - QString msg = QString::fromLocal8Bit(response.data.findChild("msg").data()); - //QTC_ASSERT(status() == InferiorRunning, /**/); - //interruptInferior(); - emit inferiorStartFailed(msg); - } -} - -void PlainGdbAdapter::interruptInferior() -{ - const qint64 attachedPID = m_engine->inferiorPid(); - if (attachedPID <= 0) { - debugMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED")); - return; - } - - if (!interruptProcess(attachedPID)) - debugMessage(_("CANNOT INTERRUPT %1").arg(attachedPID)); -} - -void PlainGdbAdapter::shutdown() -{ - debugMessage(_("PLAIN ADAPTER SHUTDOWN %1").arg(state())); - m_outputCollector.shutdown(); -} - -} // namespace Internal } // namespace Debugger +} // namespace Internal diff --git a/src/plugins/debugger/gdb/abstractplaingdbadapter.h b/src/plugins/debugger/gdb/abstractplaingdbadapter.h new file mode 100644 index 00000000000..9d6fddcc6e0 --- /dev/null +++ b/src/plugins/debugger/gdb/abstractplaingdbadapter.h @@ -0,0 +1,62 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ABSTRACTPLAINGDBADAPTER_H +#define ABSTRACTPLAINGDBADAPTER_H + +#include "abstractgdbadapter.h" + +namespace Debugger { +namespace Internal { + +class AbstractPlainGdbAdapter : public AbstractGdbAdapter +{ +public: + AbstractPlainGdbAdapter(GdbEngine *engine, QObject *parent = 0); + + virtual void startInferior(); + virtual void startInferiorPhase2(); + +protected: + void handleInfoTarget(const GdbResponse &response); + +private: + virtual QByteArray execFilePath() const = 0; + virtual bool infoTargetNecessary() const = 0; + virtual QByteArray toLocalEncoding(const QString &s) const = 0; + virtual QString fromLocalEncoding(const QByteArray &b) const = 0; + void handleExecRun(const GdbResponse &response); + void handleFileExecAndSymbols(const GdbResponse &response); + +}; + +} // namespace Debugger +} // namespace Internal + +#endif // ABSTRACTPLAINGDBADAPTER_H diff --git a/src/plugins/debugger/gdb/attachgdbadapter.h b/src/plugins/debugger/gdb/attachgdbadapter.h index 923231d9ef8..50377d73d53 100644 --- a/src/plugins/debugger/gdb/attachgdbadapter.h +++ b/src/plugins/debugger/gdb/attachgdbadapter.h @@ -32,6 +32,8 @@ #include "abstractgdbadapter.h" +#include "abstractgdbprocess.h" + namespace Debugger { namespace Internal { @@ -54,9 +56,12 @@ public: void startInferior(); void interruptInferior(); const char *inferiorShutdownCommand() const { return "detach"; } + AbstractGdbProcess *gdbProc() { return &m_gdbProc; } private: void handleAttach(const GdbResponse &response); + + LocalGdbProcess m_gdbProc; }; } // namespace Internal diff --git a/src/plugins/debugger/gdb/classicgdbengine.cpp b/src/plugins/debugger/gdb/classicgdbengine.cpp index 5bed94cc426..84066bc534b 100644 --- a/src/plugins/debugger/gdb/classicgdbengine.cpp +++ b/src/plugins/debugger/gdb/classicgdbengine.cpp @@ -513,8 +513,9 @@ void GdbEngine::tryLoadDebuggingHelpersClassic() m_debuggingHelperState = DebuggingHelperLoadTried; QByteArray dlopenLib; - if (startParameters().startMode == StartRemote) - dlopenLib = startParameters().remoteDumperLib.toLocal8Bit(); + if (startParameters().startMode == AttachToRemote + || startParameters().startMode == StartRemoteGdb) + dlopenLib = startParameters().remoteDumperLib; else dlopenLib = manager()->qtDumperLibraryName().toLocal8Bit(); diff --git a/src/plugins/debugger/gdb/coregdbadapter.h b/src/plugins/debugger/gdb/coregdbadapter.h index 24d641e5c47..72aa161ec8a 100644 --- a/src/plugins/debugger/gdb/coregdbadapter.h +++ b/src/plugins/debugger/gdb/coregdbadapter.h @@ -32,6 +32,8 @@ #include "abstractgdbadapter.h" +#include "abstractgdbprocess.h" + #ifdef Q_OS_LINUX # define EXE_FROM_CORE #endif @@ -57,6 +59,7 @@ public: void startAdapter(); void startInferior(); void interruptInferior(); + AbstractGdbProcess *gdbProc() { return &m_gdbProc; } private: void loadExeAndSyms(); @@ -68,6 +71,7 @@ private: int m_round; #endif QString m_executable; + LocalGdbProcess m_gdbProc; }; } // namespace Internal diff --git a/src/plugins/debugger/gdb/gdb.pri b/src/plugins/debugger/gdb/gdb.pri index 85b9ef944e0..ffe58619521 100644 --- a/src/plugins/debugger/gdb/gdb.pri +++ b/src/plugins/debugger/gdb/gdb.pri @@ -6,11 +6,16 @@ HEADERS += \ $$PWD/abstractgdbadapter.h \ $$PWD/attachgdbadapter.h \ $$PWD/coregdbadapter.h \ - $$PWD/plaingdbadapter.h \ + $$PWD/localplaingdbadapter.h \ $$PWD/termgdbadapter.h \ - $$PWD/remotegdbadapter.h \ + $$PWD/remotegdbserveradapter.h \ $$PWD/trkgdbadapter.h \ - $$PWD/s60debuggerbluetoothstarter.h + $$PWD/s60debuggerbluetoothstarter.h \ + $$PWD/abstractgdbprocess.h \ + $$PWD/localgdbprocess.h \ + $$PWD/remotegdbprocess.h \ + $$PWD/remoteplaingdbadapter.h \ + $$PWD/abstractplaingdbadapter.h SOURCES += \ $$PWD/gdbmi.cpp \ @@ -22,11 +27,16 @@ SOURCES += \ $$PWD/abstractgdbadapter.cpp \ $$PWD/attachgdbadapter.cpp \ $$PWD/coregdbadapter.cpp \ - $$PWD/plaingdbadapter.cpp \ + $$PWD/localplaingdbadapter.cpp \ $$PWD/termgdbadapter.cpp \ - $$PWD/remotegdbadapter.cpp \ + $$PWD/remotegdbserveradapter.cpp \ $$PWD/trkgdbadapter.cpp \ - $$PWD/s60debuggerbluetoothstarter.cpp + $$PWD/s60debuggerbluetoothstarter.cpp \ + $$PWD/abstractgdbprocess.cpp \ + $$PWD/localgdbprocess.cpp \ + $$PWD/remotegdbprocess.cpp \ + $$PWD/remoteplaingdbadapter.cpp \ + $$PWD/abstractplaingdbadapter.cpp FORMS += $$PWD/gdboptionspage.ui diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 58f01e7318b..3b7dd84654b 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -36,9 +36,10 @@ #include "attachgdbadapter.h" #include "coregdbadapter.h" -#include "plaingdbadapter.h" +#include "localplaingdbadapter.h" #include "termgdbadapter.h" -#include "remotegdbadapter.h" +#include "remotegdbserveradapter.h" +#include "remoteplaingdbadapter.h" #include "trkgdbadapter.h" #include "watchutils.h" @@ -218,10 +219,16 @@ QMainWindow *GdbEngine::mainWindow() const return DebuggerUISwitcher::instance()->mainWindow(); } +AbstractGdbProcess *GdbEngine::gdbProc() const +{ + return m_gdbAdapter->gdbProc(); +} + GdbEngine::~GdbEngine() { // Prevent sending error messages afterwards. - disconnect(&m_gdbProc, 0, this, 0); + if (m_gdbAdapter) + disconnect(gdbProc(), 0, this, 0); delete m_gdbAdapter; m_gdbAdapter = 0; } @@ -614,7 +621,7 @@ void GdbEngine::handleResponse(const QByteArray &buff) void GdbEngine::readGdbStandardError() { - QByteArray err = m_gdbProc.readAllStandardError(); + QByteArray err = gdbProc()->readAllStandardError(); debugMessage(_("UNEXPECTED GDB STDERR: " + err)); if (err == "Undefined command: \"bb\". Try \"help\".\n") return; @@ -631,7 +638,7 @@ void GdbEngine::readGdbStandardOutput() int newstart = 0; int scan = m_inbuffer.size(); - m_inbuffer.append(m_gdbProc.readAllStandardOutput()); + m_inbuffer.append(gdbProc()->readAllStandardOutput()); // This can trigger when a dialog starts a nested event loop if (m_busy) @@ -873,7 +880,7 @@ void GdbEngine::commandTimeout() debugMessage(_("KILLING DEBUGGER AS REQUESTED BY USER")); // This is an undefined state, so we just pull the emergency brake. manager()->watchHandler()->endCycle(); - m_gdbProc.kill(); + gdbProc()->kill(); } else { debugMessage(_("CONTINUE DEBUGGER AS REQUESTED BY USER")); } @@ -1333,7 +1340,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data) && reason == "signal-received") { QByteArray name = data.findChild("signal-name").data(); if (name != STOP_SIGNAL - && (startParameters().startMode != StartRemote + && (startParameters().startMode != AttachToRemote || name != CROSS_STOP_SIGNAL)) initHelpers = false; } @@ -1400,7 +1407,7 @@ void GdbEngine::handleStop1(const GdbMi &data) // Ignore these as they are showing up regularly when // stopping debugging. if (name != STOP_SIGNAL - && (startParameters().startMode != StartRemote + && (startParameters().startMode != AttachToRemote || name != CROSS_STOP_SIGNAL)) { QString msg = tr("

The inferior stopped because it received a " "signal from the Operating System.

" @@ -1629,7 +1636,7 @@ void GdbEngine::shutdown() m_gdbAdapter->shutdown(); // fall-through case AdapterStartFailed: // Adapter "did something", but it did not help - if (m_gdbProc.state() == QProcess::Running) { + if (gdbProc()->state() == QProcess::Running) { m_commandsToRunOnTemporaryBreak.clear(); postCommand("-gdb-exit", GdbEngine::ExitRequest, CB(handleGdbExit)); } else { @@ -1658,7 +1665,7 @@ void GdbEngine::shutdown() // fall-through case InferiorStopFailed: // Tough luck, I guess. But unreachable as of now anyway. setState(EngineShuttingDown); - m_gdbProc.kill(); + gdbProc()->kill(); break; } } @@ -1698,7 +1705,7 @@ void GdbEngine::handleGdbExit(const GdbResponse &response) QString msg = m_gdbAdapter->msgGdbStopFailed( QString::fromLocal8Bit(response.data.findChild("msg").data())); debugMessage(_("GDB WON'T EXIT (%1); KILLING IT").arg(msg)); - m_gdbProc.kill(); + gdbProc()->kill(); } } @@ -1722,7 +1729,7 @@ void GdbEngine::abortDebugger() // called from the manager { disconnectDebuggingHelperActions(); shutdown(); - m_gdbProc.kill(); + gdbProc()->kill(); } int GdbEngine::currentFrame() const @@ -1766,14 +1773,16 @@ AbstractGdbAdapter *GdbEngine::createAdapter(const DebuggerStartParametersPtr &s switch (sp->startMode) { case AttachCore: return new CoreGdbAdapter(this); - case StartRemote: - return new RemoteGdbAdapter(this, sp->toolChainType); + case AttachToRemote: + return new RemoteGdbServerAdapter(this, sp->toolChainType); + case StartRemoteGdb: + return new RemotePlainGdbAdapter(this); case AttachExternal: return new AttachGdbAdapter(this); default: if (sp->useTerminal) return new TermGdbAdapter(this); - return new PlainGdbAdapter(this); + return new LocalPlainGdbAdapter(this); } } @@ -3929,7 +3938,7 @@ void GdbEngine::gotoLocation(const StackFrame &frame, bool setMarker) bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QString &settingsIdHint) { - m_gdbProc.disconnect(); // From any previous runs + gdbProc()->disconnect(); // From any previous runs m_gdb = QString::fromLatin1(qgetenv("QTC_DEBUGGER_PATH")); if (m_gdb.isEmpty()) @@ -3955,7 +3964,7 @@ bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QStr const QString winPythonVersion = QLatin1String(winPythonVersionC); const QDir dir = fi.absoluteDir(); if (dir.exists(winPythonVersion)) { - QProcessEnvironment environment = m_gdbProc.processEnvironment(); + QProcessEnvironment environment = gdbProc()->processEnvironment(); const QString pythonPathVariable = QLatin1String("PYTHONPATH"); // Check for existing values. if (environment.contains(pythonPathVariable)) { @@ -3968,7 +3977,7 @@ bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QStr environment.insert(pythonPathVariable, pythonPath); manager()->showDebuggerOutput(LogMisc, _("Python path: %1").arg(pythonPath)); - m_gdbProc.setProcessEnvironment(environment); + gdbProc()->setProcessEnvironment(environment); } foundPython = true; } @@ -3979,20 +3988,20 @@ bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QStr } #endif - connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), + connect(gdbProc(), SIGNAL(error(QProcess::ProcessError)), SLOT(handleGdbError(QProcess::ProcessError))); - connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)), + connect(gdbProc(), SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(handleGdbFinished(int, QProcess::ExitStatus))); - connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()), + connect(gdbProc(), SIGNAL(readyReadStandardOutput()), SLOT(readGdbStandardOutput())); - connect(&m_gdbProc, SIGNAL(readyReadStandardError()), + connect(gdbProc(), SIGNAL(readyReadStandardError()), SLOT(readGdbStandardError())); - m_gdbProc.start(m_gdb, gdbArgs); + gdbProc()->start(m_gdb, gdbArgs); - if (!m_gdbProc.waitForStarted()) { + if (!gdbProc()->waitForStarted()) { const QString msg = tr("Unable to start gdb '%1': %2") - .arg(m_gdb, m_gdbProc.errorString()); + .arg(m_gdb, gdbProc()->errorString()); handleAdapterStartFailed(msg, settingsIdHint); return false; } @@ -4114,7 +4123,7 @@ void GdbEngine::handleGdbError(QProcess::ProcessError error) case QProcess::WriteError: case QProcess::Timedout: default: - m_gdbProc.kill(); + gdbProc()->kill(); setState(EngineShuttingDown, true); showMessageBox(QMessageBox::Critical, tr("Gdb I/O Error"), errorMessage(error)); @@ -4230,7 +4239,7 @@ void GdbEngine::handleAdapterCrashed(const QString &msg) setState(AdapterStartFailed, true); // No point in being friendly here ... - m_gdbProc.kill(); + gdbProc()->kill(); if (!msg.isEmpty()) showMessageBox(QMessageBox::Critical, tr("Adapter crashed"), msg); diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index b51c5ec3204..17f7aef6b5c 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -33,6 +33,7 @@ #include "idebuggerengine.h" #include "debuggermanager.h" // only for StartParameters #include "gdbmi.h" +#include "localgdbprocess.h" #include "watchutils.h" #include @@ -41,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -60,6 +60,7 @@ class DebuggerManager; namespace Internal { class AbstractGdbAdapter; +class AbstractGdbProcess; class GdbResponse; class GdbMi; @@ -69,8 +70,8 @@ class DisassemblerAgentCookie; class AttachGdbAdapter; class CoreGdbAdapter; -class PlainGdbAdapter; -class RemoteGdbAdapter; +class LocalPlainGdbAdapter; +class RemoteGdbServerAdapter; class TrkGdbAdapter; enum DebuggingHelperState @@ -95,11 +96,13 @@ public: private: friend class AbstractGdbAdapter; + friend class AbstractPlainGdbAdapter; friend class AttachGdbAdapter; friend class CoreGdbAdapter; - friend class PlainGdbAdapter; + friend class LocalPlainGdbAdapter; friend class TermGdbAdapter; - friend class RemoteGdbAdapter; + friend class RemoteGdbServerAdapter; + friend class RemotePlainGdbAdapter; friend class TrkGdbAdapter; private: ////////// General Interface ////////// @@ -165,7 +168,6 @@ private: QByteArray m_inbuffer; bool m_busy; - QProcess m_gdbProc; AbstractGdbAdapter *m_gdbAdapter; private: ////////// Gdb Command Management ////////// @@ -524,6 +526,7 @@ private: ////////// Convenience Functions ////////// int buttons = 0); void debugMessage(const QString &msg); QMainWindow *mainWindow() const; + AbstractGdbProcess *gdbProc() const; static QString m_toolTipExpression; static QPoint m_toolTipPos; diff --git a/src/plugins/debugger/gdb/localgdbprocess.cpp b/src/plugins/debugger/gdb/localgdbprocess.cpp new file mode 100644 index 00000000000..60bb343526d --- /dev/null +++ b/src/plugins/debugger/gdb/localgdbprocess.cpp @@ -0,0 +1,108 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "localgdbprocess.h" + +namespace Debugger { +namespace Internal { + +LocalGdbProcess::LocalGdbProcess(QObject *parent) : AbstractGdbProcess(parent) +{ + connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), + this, SIGNAL(error(QProcess::ProcessError))); + connect(&m_gdbProc, SIGNAL(finished(int,QProcess::ExitStatus)), + this, SIGNAL(finished(int,QProcess::ExitStatus))); + connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()), + this, SIGNAL(readyReadStandardOutput())); + connect(&m_gdbProc, SIGNAL(readyReadStandardError()), + this, SIGNAL(readyReadStandardError())); +} + +QByteArray LocalGdbProcess::readAllStandardOutput() +{ + return m_gdbProc.readAllStandardOutput(); +} + +QByteArray LocalGdbProcess::readAllStandardError() +{ + return m_gdbProc.readAllStandardError(); +} + +void LocalGdbProcess::start(const QString &cmd, const QStringList &args) +{ + m_gdbProc.start(cmd, args); +} + +bool LocalGdbProcess::waitForStarted() +{ + return m_gdbProc.waitForStarted(); +} + +qint64 LocalGdbProcess::write(const QByteArray &data) +{ + return m_gdbProc.write(data); +} + +void LocalGdbProcess::kill() +{ + m_gdbProc.kill(); +} + +QProcess::ProcessState LocalGdbProcess::state() const +{ + return m_gdbProc.state(); +} + +QString LocalGdbProcess::errorString() const +{ + return m_gdbProc.errorString(); +} + +QProcessEnvironment LocalGdbProcess::processEnvironment() const +{ + return m_gdbProc.processEnvironment(); +} + +void LocalGdbProcess::setProcessEnvironment(const QProcessEnvironment &env) +{ + m_gdbProc.setProcessEnvironment(env); +} + +void LocalGdbProcess::setEnvironment(const QStringList &env) +{ + m_gdbProc.setEnvironment(env); +} + +void LocalGdbProcess::setWorkingDirectory(const QString &dir) +{ + m_gdbProc.setWorkingDirectory(dir); +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/gdb/localgdbprocess.h b/src/plugins/debugger/gdb/localgdbprocess.h new file mode 100644 index 00000000000..3ab57739d64 --- /dev/null +++ b/src/plugins/debugger/gdb/localgdbprocess.h @@ -0,0 +1,66 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef LOCALGDBPROCESS_H +#define LOCALGDBPROCESS_H + +#include "abstractgdbprocess.h" + +namespace Debugger { +namespace Internal { + +class LocalGdbProcess : public AbstractGdbProcess +{ +public: + explicit LocalGdbProcess(QObject *parent = 0); + + virtual QByteArray readAllStandardOutput(); + virtual QByteArray readAllStandardError(); + + virtual void start(const QString &cmd, const QStringList &args); + virtual bool waitForStarted(); + virtual qint64 write(const QByteArray &data); + virtual void kill(); + + virtual QProcess::ProcessState state() const; + virtual QString errorString() const; + + virtual QProcessEnvironment processEnvironment() const; + virtual void setProcessEnvironment(const QProcessEnvironment &env); + virtual void setEnvironment(const QStringList &env); + virtual void setWorkingDirectory(const QString &dir); + +private: + QProcess m_gdbProc; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // LOCALGDBPROCESS_H diff --git a/src/plugins/debugger/gdb/localplaingdbadapter.cpp b/src/plugins/debugger/gdb/localplaingdbadapter.cpp new file mode 100644 index 00000000000..7c370cb9467 --- /dev/null +++ b/src/plugins/debugger/gdb/localplaingdbadapter.cpp @@ -0,0 +1,139 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "localplaingdbadapter.h" + +#include "gdbengine.h" +#include "procinterrupt.h" +#include "debuggerstringutils.h" + +#include + +#include + +namespace Debugger { +namespace Internal { + +/////////////////////////////////////////////////////////////////////// +// +// PlainGdbAdapter +// +/////////////////////////////////////////////////////////////////////// + +LocalPlainGdbAdapter::LocalPlainGdbAdapter(GdbEngine *engine, QObject *parent) + : AbstractPlainGdbAdapter(engine, parent) +{ + // Output + connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)), + engine, SLOT(readDebugeeOutput(QByteArray))); +} + +AbstractGdbAdapter::DumperHandling LocalPlainGdbAdapter::dumperHandling() const +{ + // LD_PRELOAD fails for System-Qt on Mac. +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) + return DumperLoadedByGdb; +#else + return DumperLoadedByGdbPreload; +#endif +} + +void LocalPlainGdbAdapter::startAdapter() +{ + QTC_ASSERT(state() == EngineStarting, qDebug() << state()); + setState(AdapterStarting); + debugMessage(_("TRYING TO START ADAPTER")); + + QStringList gdbArgs; + + if (!m_outputCollector.listen()) { + emit adapterStartFailed(tr("Cannot set up communication with child process: %1") + .arg(m_outputCollector.errorString()), QString()); + return; + } + gdbArgs.append(_("--tty=") + m_outputCollector.serverName()); + + if (!startParameters().workingDir.isEmpty()) + m_gdbProc.setWorkingDirectory(startParameters().workingDir); + if (!startParameters().environment.isEmpty()) + m_gdbProc.setEnvironment(startParameters().environment); + + if (!m_engine->startGdb(gdbArgs)) { + m_outputCollector.shutdown(); + return; + } + + emit adapterStarted(); +} + +void LocalPlainGdbAdapter::interruptInferior() +{ + const qint64 attachedPID = m_engine->inferiorPid(); + if (attachedPID <= 0) { + debugMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED")); + return; + } + + if (!interruptProcess(attachedPID)) + debugMessage(_("CANNOT INTERRUPT %1").arg(attachedPID)); +} + +void LocalPlainGdbAdapter::shutdown() +{ + debugMessage(_("PLAIN ADAPTER SHUTDOWN %1").arg(state())); + m_outputCollector.shutdown(); +} + +QByteArray LocalPlainGdbAdapter::execFilePath() const +{ + return QFileInfo(startParameters().executable) + .absoluteFilePath().toLocal8Bit(); +} + +bool LocalPlainGdbAdapter::infoTargetNecessary() const +{ +#ifdef Q_OS_LINUX + return true; +#else + return false; +#endif +} + +QByteArray LocalPlainGdbAdapter::toLocalEncoding(const QString &s) const +{ + return s.toLocal8Bit(); +} + +QString LocalPlainGdbAdapter::fromLocalEncoding(const QByteArray &b) const +{ + return QString::fromLocal8Bit(b); +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/gdb/plaingdbadapter.h b/src/plugins/debugger/gdb/localplaingdbadapter.h similarity index 78% rename from src/plugins/debugger/gdb/plaingdbadapter.h rename to src/plugins/debugger/gdb/localplaingdbadapter.h index 83e6aff36cd..4b9f6a69d36 100644 --- a/src/plugins/debugger/gdb/plaingdbadapter.h +++ b/src/plugins/debugger/gdb/localplaingdbadapter.h @@ -30,7 +30,9 @@ #ifndef DEBUGGER_PLAINGDBADAPTER_H #define DEBUGGER_PLAINGDBADAPTER_H -#include "abstractgdbadapter.h" +#include "abstractplaingdbadapter.h" + +#include "abstractgdbprocess.h" #include @@ -43,30 +45,28 @@ namespace Internal { // /////////////////////////////////////////////////////////////////////// -class PlainGdbAdapter : public AbstractGdbAdapter +class LocalPlainGdbAdapter : public AbstractPlainGdbAdapter { Q_OBJECT public: - PlainGdbAdapter(GdbEngine *engine, QObject *parent = 0); + LocalPlainGdbAdapter(GdbEngine *engine, QObject *parent = 0); virtual DumperHandling dumperHandling() const; void startAdapter(); - void startInferior(); - void startInferiorPhase2(); void interruptInferior(); void shutdown(); - const char *inferiorShutdownCommand() const { return "kill"; } + AbstractGdbProcess *gdbProc() { return &m_gdbProc; } private: - void handleFileExecAndSymbols(const GdbResponse &response); - void handleExecRun(const GdbResponse &response); -#ifdef Q_OS_LINUX - void handleInfoTarget(const GdbResponse &response); -#endif + virtual QByteArray execFilePath() const; + virtual bool infoTargetNecessary() const; + virtual QByteArray toLocalEncoding(const QString &s) const; + virtual QString fromLocalEncoding(const QByteArray &b) const; OutputCollector m_outputCollector; + LocalGdbProcess m_gdbProc; }; } // namespace Internal diff --git a/src/plugins/debugger/gdb/remotegdbprocess.cpp b/src/plugins/debugger/gdb/remotegdbprocess.cpp new file mode 100644 index 00000000000..580b1d1f7d1 --- /dev/null +++ b/src/plugins/debugger/gdb/remotegdbprocess.cpp @@ -0,0 +1,333 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "remotegdbprocess.h" + +#include "remoteplaingdbadapter.h" + +namespace Debugger { +namespace Internal { + +RemoteGdbProcess::RemoteGdbProcess(const Core::SshServerInfo &server, + RemotePlainGdbAdapter *adapter, QObject *parent) + : AbstractGdbProcess(parent), m_serverInfo(server), m_adapter(adapter) +{ + +} + +QByteArray RemoteGdbProcess::readAllStandardOutput() +{ + QByteArray output = m_gdbOutput; + m_gdbOutput.clear(); + return output; +} + +QByteArray RemoteGdbProcess::readAllStandardError() +{ + QByteArray errorOutput = m_errorOutput; + m_errorOutput.clear(); + return errorOutput; +} + +void RemoteGdbProcess::start(const QString &cmd, const QStringList &args) +{ + m_gdbState = CmdNotYetSent; + m_gdbConn = Core::InteractiveSshConnection::create(m_serverInfo); + if (m_gdbConn->hasError()) + return; + m_appOutputReaderState = CmdNotYetSent; + m_appOutputConn = Core::InteractiveSshConnection::create(m_serverInfo); + if (m_appOutputConn->hasError()) + return; + m_errOutputReaderState = CmdNotYetSent; + m_errOutputConn = Core::InteractiveSshConnection::create(m_serverInfo); + if (m_errOutputConn->hasError()) + return; + m_command = cmd; + m_cmdArgs = args; + connect(m_gdbConn.data(), SIGNAL(remoteOutput(QByteArray)), + this, SLOT(handleGdbOutput(QByteArray))); + connect(m_appOutputConn.data(), SIGNAL(remoteOutput(QByteArray)), + this, SLOT(handleAppOutput(QByteArray))); + connect(m_errOutputConn.data(), SIGNAL(remoteOutput(QByteArray)), + this, SLOT(handleErrOutput(QByteArray))); + m_gdbConn->start(); + m_errOutputConn->start(); + m_appOutputConn->start(); +} + +bool RemoteGdbProcess::waitForStarted() +{ + return true; +} + +qint64 RemoteGdbProcess::write(const QByteArray &data) +{ + if (m_gdbState != CmdReceived || !m_inputToSend.isEmpty() + || !m_lastSeqNr.isEmpty()) { + m_inputToSend.enqueue(data); + return data.size(); + } else { + return sendInput(data); + } +} + +void RemoteGdbProcess::kill() +{ + stopReaders(); + Core::InteractiveSshConnection::Ptr controlConn + = Core::InteractiveSshConnection::create(m_serverInfo); + if (!controlConn->hasError()) { + if (controlConn->start()) + controlConn->sendInput("pkill -x gdb\r\n"); + } + + m_gdbConn->quit(); + emit finished(0, QProcess::CrashExit); +} + +QProcess::ProcessState RemoteGdbProcess::state() const +{ + switch (m_gdbState) { + case CmdNotYetSent: + return QProcess::NotRunning; + case CmdSent: + return QProcess::Starting; + case CmdReceived: + default: + return QProcess::Running; + } +} + +QString RemoteGdbProcess::errorString() const +{ + return m_gdbConn ? m_gdbConn->error() : QString(); +} + +void RemoteGdbProcess::handleGdbOutput(const QByteArray &output) +{ +#if 0 + qDebug("%s: output is '%s'", Q_FUNC_INFO, output.data()); +#endif + + if (m_gdbState == CmdNotYetSent) + return; + + m_currentGdbOutput += removeCarriageReturn(output); + if (!m_currentGdbOutput.endsWith('\n')) + return; + + if (m_gdbState == CmdSent) { + const int index = m_currentGdbOutput.indexOf(m_startCmdLine); + if (index != -1) + m_currentGdbOutput.remove(index, m_startCmdLine.size()); + // Note: We can't guarantee that we will match the command line, + // because the remote terminal sometimes inserts control characters. + // Otherwise we could change the state to CmdReceived here. + } + + m_gdbState = CmdReceived; + + checkForGdbExit(m_currentGdbOutput); + if (m_currentGdbOutput.contains(m_lastSeqNr + '^')) + m_lastSeqNr.clear(); + + if (m_lastSeqNr.isEmpty() && !m_inputToSend.isEmpty()) { +#if 0 + qDebug("Sending queued command: %s", m_inputToSend.head().data()); +#endif + sendInput(m_inputToSend.dequeue()); + } + + if (!m_currentGdbOutput.isEmpty()) { + const int startPos + = m_gdbOutput.isEmpty() ? findAnchor(m_currentGdbOutput) : 0; + if (startPos != -1) { + m_gdbOutput += m_currentGdbOutput.mid(startPos); + m_currentGdbOutput.clear(); + emit readyReadStandardOutput(); + } + } +} + +QProcessEnvironment RemoteGdbProcess::processEnvironment() const +{ + return QProcessEnvironment(); // TODO: Provide actual environment. +} + +void RemoteGdbProcess::setProcessEnvironment(const QProcessEnvironment &env) +{ + // TODO: Do something. +} + +void RemoteGdbProcess::setEnvironment(const QStringList &env) +{ + // TODO: Do something. +} + +void RemoteGdbProcess::setWorkingDirectory(const QString &dir) +{ + m_wd = dir; +} + +int RemoteGdbProcess::findAnchor(const QByteArray &data) const +{ + for (int pos = 0; pos < data.count(); ++pos) { + const char c = data.at(pos); + if (isdigit(c) || c == '*' || c == '+' || c == '=' || c == '~' + || c == '@' || c == '&' || c == '^') + return pos; + } + return -1; +} + +qint64 RemoteGdbProcess::sendInput(const QByteArray &data) +{ + int pos; + for (pos = 0; pos < data.size(); ++pos) + if (!isdigit(data.at(pos))) + break; + m_lastSeqNr = data.left(pos); + return m_gdbConn->sendInput(data) ? data.size() : 0; +} + +void RemoteGdbProcess::handleAppOutput(const QByteArray &output) +{ + if (!handleAppOrErrOutput(m_appOutputConn, m_appOutputReaderState, + m_initialAppOutput, AppOutputFile, output)) + m_adapter->handleApplicationOutput(output); +} + +void RemoteGdbProcess::handleErrOutput(const QByteArray &output) +{ + if (!handleAppOrErrOutput(m_errOutputConn, m_errOutputReaderState, + m_initialErrOutput, ErrOutputFile, output)) { + m_errorOutput += output; + emit readyReadStandardError(); + } +} + +bool RemoteGdbProcess::handleAppOrErrOutput(Core::InteractiveSshConnection::Ptr &conn, + CmdState &cmdState, QByteArray &initialOutput, const QByteArray &file, + const QByteArray &output) +{ + const QByteArray cmdLine1 = mkFifoCmdLine(file); + const QByteArray cmdLine2 = readerCmdLine(file); + if (cmdState == CmdNotYetSent) { + conn->sendInput(cmdLine1); + cmdState = CmdSent; + return true; + } + + if (cmdState == CmdSent) { + initialOutput += output; + if (initialOutput.endsWith(cmdLine2)) { + cmdState = CmdReceived; + if (m_appOutputReaderState == m_errOutputReaderState + && m_gdbState == CmdNotYetSent) + startGdb(); + } else if (initialOutput.contains(cmdLine1) + && !initialOutput.endsWith(cmdLine1)) { + initialOutput.clear(); + conn->sendInput(cmdLine2); + } + return true; + } + + return false; +} + +void RemoteGdbProcess::startGdb() +{ + m_startCmdLine = "stty -echo && " + m_command.toUtf8() + ' ' + + m_cmdArgs.join(QLatin1String(" ")).toUtf8() + + " -tty=" + AppOutputFile + " 2>" + ErrOutputFile + '\n'; + if (!m_wd.isEmpty()) + m_startCmdLine.prepend("cd " + m_wd.toUtf8() + " && "); + sendInput(m_startCmdLine); + m_gdbState = CmdSent; +} + +void RemoteGdbProcess::stopReaders() +{ + if (m_appOutputConn) { + disconnect(m_appOutputConn.data(), SIGNAL(remoteOutput(QByteArray)), + this, SLOT(handleAppOutput(QByteArray))); + m_appOutputConn->sendInput(CtrlC); + m_appOutputConn->quit(); + } + if (m_errOutputConn) { + disconnect(m_errOutputConn.data(), SIGNAL(remoteOutput(QByteArray)), + this, SLOT(handleErrOutput(QByteArray))); + m_errOutputConn->sendInput(CtrlC); + m_errOutputConn->quit(); + } +} + +QByteArray RemoteGdbProcess::mkFifoCmdLine(const QByteArray &file) +{ + return "rm -f " + file + " && mkfifo " + file + "\r\n"; +} + +QByteArray RemoteGdbProcess::readerCmdLine(const QByteArray &file) +{ + return "cat " + file + "\r\n"; +} + +QByteArray RemoteGdbProcess::removeCarriageReturn(const QByteArray &data) +{ + QByteArray output; + for (int i = 0; i < data.size(); ++i) { + const char c = data.at(i); + if (c != '\r') + output += c; + } + return output; +} + +void RemoteGdbProcess::checkForGdbExit(QByteArray &output) +{ + const QByteArray exitString("^exit"); + const int exitPos = output.indexOf(exitString); + if (exitPos != -1) { + disconnect(m_gdbConn.data(), SIGNAL(remoteOutput(QByteArray)), + this, SLOT(handleGdbOutput(QByteArray))); + output.remove(exitPos + exitString.size(), output.size()); + stopReaders(); + emit finished(0, QProcess::NormalExit); + } +} + + +const QByteArray RemoteGdbProcess::CtrlC = QByteArray(1, 0x3); +const QByteArray RemoteGdbProcess::AppOutputFile("app_output"); +const QByteArray RemoteGdbProcess::ErrOutputFile("err_output"); + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/gdb/remotegdbprocess.h b/src/plugins/debugger/gdb/remotegdbprocess.h new file mode 100644 index 00000000000..cf22c51f9d2 --- /dev/null +++ b/src/plugins/debugger/gdb/remotegdbprocess.h @@ -0,0 +1,120 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef REMOTEGDBPROCESS_H +#define REMOTEGDBPROCESS_H + +#include "abstractgdbprocess.h" + +#include + +#include +#include + +namespace Debugger { +namespace Internal { + +class RemotePlainGdbAdapter; + +class RemoteGdbProcess : public AbstractGdbProcess +{ + Q_OBJECT +public: + RemoteGdbProcess(const Core::SshServerInfo &server, + RemotePlainGdbAdapter *adapter, QObject *parent = 0); + + virtual QByteArray readAllStandardOutput(); + virtual QByteArray readAllStandardError(); + + virtual void start(const QString &cmd, const QStringList &args); + virtual bool waitForStarted(); + virtual qint64 write(const QByteArray &data); + virtual void kill(); + + virtual QProcess::ProcessState state() const; + virtual QString errorString() const; + + virtual QProcessEnvironment processEnvironment() const; + virtual void setProcessEnvironment(const QProcessEnvironment &env); + virtual void setEnvironment(const QStringList &env); + virtual void setWorkingDirectory(const QString &dir); + + static const QByteArray CtrlC; + +private slots: + void handleGdbOutput(const QByteArray &output); + void handleAppOutput(const QByteArray &output); + void handleErrOutput(const QByteArray &output); + +private: + enum CmdState { CmdNotYetSent, CmdSent, CmdReceived }; + + static QByteArray mkFifoCmdLine(const QByteArray &file); + static QByteArray readerCmdLine(const QByteArray &file); + + int findAnchor(const QByteArray &data) const; + bool handleAppOrErrOutput(Core::InteractiveSshConnection::Ptr &conn, + CmdState &cmdState, QByteArray &initialOutput, + const QByteArray &file, const QByteArray &output); + qint64 sendInput(const QByteArray &data); + void startGdb(); + void stopReaders(); + QByteArray removeCarriageReturn(const QByteArray &data); + void checkForGdbExit(QByteArray &output); + + static const QByteArray AppOutputFile; + static const QByteArray ErrOutputFile; + + Core::SshServerInfo m_serverInfo; + Core::InteractiveSshConnection::Ptr m_gdbConn; + Core::InteractiveSshConnection::Ptr m_appOutputConn; + Core::InteractiveSshConnection::Ptr m_errOutputConn; + QByteArray m_gdbOutput; + QByteArray m_errorOutput; + QString m_command; + QStringList m_cmdArgs; + QString m_wd; + QQueue m_inputToSend; + QByteArray m_currentGdbOutput; + QByteArray m_initialAppOutput; + QByteArray m_initialErrOutput; + QByteArray m_lastSeqNr; + QByteArray m_startCmdLine; + + CmdState m_gdbState; + CmdState m_appOutputReaderState; + CmdState m_errOutputReaderState; + + RemotePlainGdbAdapter *m_adapter; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // REMOTEGDBPROCESS_H diff --git a/src/plugins/debugger/gdb/remotegdbadapter.cpp b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp similarity index 89% rename from src/plugins/debugger/gdb/remotegdbadapter.cpp rename to src/plugins/debugger/gdb/remotegdbserveradapter.cpp index 8687f86d61e..37ad747a6c3 100644 --- a/src/plugins/debugger/gdb/remotegdbadapter.cpp +++ b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp @@ -27,7 +27,7 @@ ** **************************************************************************/ -#include "remotegdbadapter.h" +#include "remotegdbserveradapter.h" #include "debuggerstringutils.h" #include "gdbengine.h" @@ -43,7 +43,7 @@ namespace Debugger { namespace Internal { #define CB(callback) \ - static_cast(&RemoteGdbAdapter::callback), \ + static_cast(&RemoteGdbServerAdapter::callback), \ STRINGIFY(callback) /////////////////////////////////////////////////////////////////////// @@ -52,7 +52,7 @@ namespace Internal { // /////////////////////////////////////////////////////////////////////// -RemoteGdbAdapter::RemoteGdbAdapter(GdbEngine *engine, int toolChainType, QObject *parent) : +RemoteGdbServerAdapter::RemoteGdbServerAdapter(GdbEngine *engine, int toolChainType, QObject *parent) : AbstractGdbAdapter(engine, parent), m_toolChainType(toolChainType) { @@ -64,7 +64,7 @@ RemoteGdbAdapter::RemoteGdbAdapter(GdbEngine *engine, int toolChainType, QObject this, SLOT(readUploadStandardError())); } -AbstractGdbAdapter::DumperHandling RemoteGdbAdapter::dumperHandling() const +AbstractGdbAdapter::DumperHandling RemoteGdbServerAdapter::dumperHandling() const { switch (m_toolChainType) { case ProjectExplorer::ToolChain::MinGW: @@ -82,7 +82,7 @@ AbstractGdbAdapter::DumperHandling RemoteGdbAdapter::dumperHandling() const return DumperLoadedByGdbPreload; } -void RemoteGdbAdapter::startAdapter() +void RemoteGdbServerAdapter::startAdapter() { QTC_ASSERT(state() == EngineStarting, qDebug() << state()); setState(AdapterStarting); @@ -105,7 +105,7 @@ void RemoteGdbAdapter::startAdapter() emit adapterStarted(); } -void RemoteGdbAdapter::uploadProcError(QProcess::ProcessError error) +void RemoteGdbServerAdapter::uploadProcError(QProcess::ProcessError error) { QString msg; switch (error) { @@ -139,19 +139,19 @@ void RemoteGdbAdapter::uploadProcError(QProcess::ProcessError error) showMessageBox(QMessageBox::Critical, tr("Error"), msg); } -void RemoteGdbAdapter::readUploadStandardOutput() +void RemoteGdbServerAdapter::readUploadStandardOutput() { QByteArray ba = m_uploadProc.readAllStandardOutput(); m_engine->showDebuggerOutput(LogOutput, QString::fromLocal8Bit(ba, ba.length())); } -void RemoteGdbAdapter::readUploadStandardError() +void RemoteGdbServerAdapter::readUploadStandardError() { QByteArray ba = m_uploadProc.readAllStandardError(); m_engine->showDebuggerOutput(LogError, QString::fromLocal8Bit(ba, ba.length())); } -void RemoteGdbAdapter::startInferior() +void RemoteGdbServerAdapter::startInferior() { QTC_ASSERT(state() == InferiorStarting, qDebug() << state()); @@ -176,14 +176,14 @@ void RemoteGdbAdapter::startInferior() CB(handleFileExecAndSymbols)); } -void RemoteGdbAdapter::handleSetTargetAsync(const GdbResponse &response) +void RemoteGdbServerAdapter::handleSetTargetAsync(const GdbResponse &response) { QTC_ASSERT(state() == InferiorStarting, qDebug() << state()); if (response.resultClass == GdbResultError) qDebug() << "Adapter too old: does not support asynchronous mode."; } -void RemoteGdbAdapter::handleFileExecAndSymbols(const GdbResponse &response) +void RemoteGdbServerAdapter::handleFileExecAndSymbols(const GdbResponse &response) { QTC_ASSERT(state() == InferiorStarting, qDebug() << state()); if (response.resultClass == GdbResultDone) { @@ -203,7 +203,7 @@ void RemoteGdbAdapter::handleFileExecAndSymbols(const GdbResponse &response) } } -void RemoteGdbAdapter::handleTargetRemote(const GdbResponse &record) +void RemoteGdbServerAdapter::handleTargetRemote(const GdbResponse &record) { QTC_ASSERT(state() == InferiorStarting, qDebug() << state()); if (record.resultClass == GdbResultDone) { @@ -220,19 +220,19 @@ void RemoteGdbAdapter::handleTargetRemote(const GdbResponse &record) } } -void RemoteGdbAdapter::startInferiorPhase2() +void RemoteGdbServerAdapter::startInferiorPhase2() { m_engine->continueInferiorInternal(); } -void RemoteGdbAdapter::interruptInferior() +void RemoteGdbServerAdapter::interruptInferior() { // FIXME: On some gdb versions like git 170ffa5d7dd this produces // >810^error,msg="mi_cmd_exec_interrupt: Inferior not executing." m_engine->postCommand("-exec-interrupt", GdbEngine::Immediate); } -void RemoteGdbAdapter::shutdown() +void RemoteGdbServerAdapter::shutdown() { // FIXME: cleanup missing } diff --git a/src/plugins/debugger/gdb/remotegdbadapter.h b/src/plugins/debugger/gdb/remotegdbserveradapter.h similarity index 89% rename from src/plugins/debugger/gdb/remotegdbadapter.h rename to src/plugins/debugger/gdb/remotegdbserveradapter.h index 76b797062b7..3a0d869d1f2 100644 --- a/src/plugins/debugger/gdb/remotegdbadapter.h +++ b/src/plugins/debugger/gdb/remotegdbserveradapter.h @@ -32,6 +32,8 @@ #include "abstractgdbadapter.h" +#include "abstractgdbprocess.h" + namespace Debugger { namespace Internal { @@ -41,12 +43,12 @@ namespace Internal { // /////////////////////////////////////////////////////////////////////// -class RemoteGdbAdapter : public AbstractGdbAdapter +class RemoteGdbServerAdapter : public AbstractGdbAdapter { Q_OBJECT public: - RemoteGdbAdapter(GdbEngine *engine, int toolChainType, QObject *parent = 0); + RemoteGdbServerAdapter(GdbEngine *engine, int toolChainType, QObject *parent = 0); virtual DumperHandling dumperHandling() const; @@ -55,6 +57,7 @@ public: void startInferiorPhase2(); void interruptInferior(); void shutdown(); + AbstractGdbProcess *gdbProc() { return &m_gdbProc; } private: Q_SLOT void readUploadStandardOutput(); @@ -68,6 +71,7 @@ private: const int m_toolChainType; QProcess m_uploadProc; + LocalGdbProcess m_gdbProc; }; } // namespace Internal diff --git a/src/plugins/debugger/gdb/remoteplaingdbadapter.cpp b/src/plugins/debugger/gdb/remoteplaingdbadapter.cpp new file mode 100644 index 00000000000..4bddbbe5786 --- /dev/null +++ b/src/plugins/debugger/gdb/remoteplaingdbadapter.cpp @@ -0,0 +1,93 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "remoteplaingdbadapter.h" + +#include +#include +#include + + +namespace Debugger { +namespace Internal { + +RemotePlainGdbAdapter::RemotePlainGdbAdapter(GdbEngine *engine, + QObject *parent) + : AbstractPlainGdbAdapter(engine, parent), + m_gdbProc(engine->startParameters().sshserver, this) +{ +} + +void RemotePlainGdbAdapter::startAdapter() +{ + QTC_ASSERT(state() == EngineStarting, qDebug() << state()); + setState(AdapterStarting); + debugMessage(QLatin1String("TRYING TO START ADAPTER")); + + if (!startParameters().workingDir.isEmpty()) + m_gdbProc.setWorkingDirectory(startParameters().workingDir); + if (!startParameters().environment.isEmpty()) + m_gdbProc.setEnvironment(startParameters().environment); + + if (m_engine->startGdb(QStringList(), m_engine->startParameters().debuggerCommand)) + emit adapterStarted(); +} + +void RemotePlainGdbAdapter::interruptInferior() +{ + m_gdbProc.write(RemoteGdbProcess::CtrlC); +} + +QByteArray RemotePlainGdbAdapter::execFilePath() const +{ + return startParameters().executable.toUtf8(); +} + +bool RemotePlainGdbAdapter::infoTargetNecessary() const +{ + return true; +} + +QByteArray RemotePlainGdbAdapter::toLocalEncoding(const QString &s) const +{ + return s.toUtf8(); +} + +QString RemotePlainGdbAdapter::fromLocalEncoding(const QByteArray &b) const +{ + return QString::fromUtf8(b); +} + +void RemotePlainGdbAdapter::handleApplicationOutput(const QByteArray &output) +{ + m_engine->manager()->showApplicationOutput(output, false); +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/gdb/remoteplaingdbadapter.h b/src/plugins/debugger/gdb/remoteplaingdbadapter.h new file mode 100644 index 00000000000..a9e089bec7a --- /dev/null +++ b/src/plugins/debugger/gdb/remoteplaingdbadapter.h @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef REMOTEGDBCLIENTADAPTER_H +#define REMOTEGDBCLIENTADAPTER_H + +#include "abstractplaingdbadapter.h" +#include "remotegdbprocess.h" + +namespace Debugger { +namespace Internal { + +class RemotePlainGdbAdapter : public AbstractPlainGdbAdapter +{ + Q_OBJECT + friend class RemoteGdbProcess; +public: + RemotePlainGdbAdapter(GdbEngine *engine, QObject *parent = 0); + + virtual void startAdapter(); + virtual void interruptInferior(); + virtual AbstractGdbProcess *gdbProc() { return &m_gdbProc; } + virtual DumperHandling dumperHandling() const { return DumperLoadedByGdbPreload; } + +private: + virtual QByteArray execFilePath() const; + virtual bool infoTargetNecessary() const; + virtual QByteArray toLocalEncoding(const QString &s) const; + virtual QString fromLocalEncoding(const QByteArray &b) const; + void handleApplicationOutput(const QByteArray &output); + + RemoteGdbProcess m_gdbProc; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // REMOTEGDBCLIENTADAPTER_H diff --git a/src/plugins/debugger/gdb/termgdbadapter.h b/src/plugins/debugger/gdb/termgdbadapter.h index 1e05cbdd0b7..54a09e4ed04 100644 --- a/src/plugins/debugger/gdb/termgdbadapter.h +++ b/src/plugins/debugger/gdb/termgdbadapter.h @@ -32,6 +32,8 @@ #include "abstractgdbadapter.h" +#include "abstractgdbprocess.h" + #include namespace Debugger { @@ -57,6 +59,7 @@ public: void startInferior(); void startInferiorPhase2(); void interruptInferior(); + AbstractGdbProcess *gdbProc() { return &m_gdbProc; } private: void handleStubAttached(const GdbResponse &response); @@ -69,6 +72,7 @@ private: Q_SLOT void stubMessage(const QString &msg, bool isError); Utils::ConsoleProcess m_stubProc; + LocalGdbProcess m_gdbProc; }; } // namespace Internal diff --git a/src/plugins/debugger/gdb/trkgdbadapter.cpp b/src/plugins/debugger/gdb/trkgdbadapter.cpp index 2c9e28c23c3..2caaf5d52a0 100644 --- a/src/plugins/debugger/gdb/trkgdbadapter.cpp +++ b/src/plugins/debugger/gdb/trkgdbadapter.cpp @@ -1929,7 +1929,7 @@ void TrkGdbAdapter::write(const QByteArray &data) trkReadMemoryMessage(m_session.dataseg, 12)); return; } - m_engine->m_gdbProc.write(data); + m_gdbProc.write(data); } uint oldPC; diff --git a/src/plugins/debugger/gdb/trkgdbadapter.h b/src/plugins/debugger/gdb/trkgdbadapter.h index a80a9556a3a..c2af38308ae 100644 --- a/src/plugins/debugger/gdb/trkgdbadapter.h +++ b/src/plugins/debugger/gdb/trkgdbadapter.h @@ -35,6 +35,7 @@ #include "trkutils.h" #include "trkdevice.h" #include "launcher.h" +#include "abstractgdbprocess.h" #include #include @@ -182,6 +183,7 @@ private: void startInferiorPhase2(); void interruptInferior(); void shutdown(); + AbstractGdbProcess *gdbProc() { return &m_gdbProc; } void cleanup(); void emitDelayedInferiorStartFailed(const QString &msg); @@ -307,6 +309,7 @@ private: QString m_symbolFile; int m_verbose; bool m_bufferedMemoryRead; + LocalGdbProcess m_gdbProc; }; } // namespace Internal diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp index 6d9983666e3..b9287805d43 100644 --- a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp +++ b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp @@ -58,6 +58,8 @@ namespace Qt4ProjectManager { namespace Internal { +#define USE_GDBSERVER + using ProjectExplorer::RunConfiguration; using ProjectExplorer::ToolChain; @@ -132,7 +134,7 @@ void AbstractMaemoRunControl::startDeployment(bool forDebugging) m_needsInstall = true; } else { m_needsInstall = false; - } + } if (forDebugging && m_runConfig->debuggingHelpersNeedDeployment(m_devConfig.server.host)) { const QFileInfo &info(m_runConfig->dumperLib()); @@ -286,7 +288,7 @@ void AbstractMaemoRunControl::handleRunThreadFinished() emit appendMessage(this, tr("Remote execution canceled due to user request."), false); - } else if (m_sshRunner->hasError()) { + } else if (m_sshRunner && m_sshRunner->hasError()) { emit appendMessage(this, tr("Error running remote process: %1") .arg(m_sshRunner->error()), true); @@ -383,22 +385,36 @@ MaemoDebugRunControl::MaemoDebugRunControl(RunConfiguration *runConfiguration) , m_startParams(new Debugger::DebuggerStartParameters) { QTC_ASSERT(m_debuggerManager != 0, return); - m_startParams->startMode = Debugger::StartRemote; +#ifdef USE_GDBSERVER + m_startParams->startMode = Debugger::AttachToRemote; m_startParams->executable = executableOnHost(); + m_startParams->debuggerCommand = m_runConfig->gdbCmd(); m_startParams->remoteChannel = m_devConfig.server.host % QLatin1Char(':') % gdbServerPort(); m_startParams->remoteArchitecture = QLatin1String("arm"); +#else + m_startParams->startMode = Debugger::StartRemoteGdb; + m_startParams->executable = executableFilePathOnTarget(); + m_startParams->debuggerCommand = QLatin1String("/usr/bin/gdb"); + m_startParams->sshserver = m_devConfig.server; +#endif + m_startParams->processArgs = m_runConfig->arguments(); m_startParams->sysRoot = m_runConfig->sysRoot(); m_startParams->toolChainType = ToolChain::GCC_MAEMO; - m_startParams->debuggerCommand = m_runConfig->gdbCmd(); m_startParams->dumperLibrary = m_runConfig->dumperLib(); - m_startParams->remoteDumperLib = QString::fromLocal8Bit("%1/%2") - .arg(remoteDir()).arg(QFileInfo(m_runConfig->dumperLib()).fileName()); + m_startParams->remoteDumperLib = remoteDir().toUtf8() + '/' + + QFileInfo(m_runConfig->dumperLib()).fileName().toUtf8(); connect(m_debuggerManager, SIGNAL(debuggingFinished()), this, SLOT(debuggingFinished()), Qt::QueuedConnection); connect(m_debuggerManager, SIGNAL(applicationOutputAvailable(QString, bool)), - this, SLOT(debuggerOutput(QString)), Qt::QueuedConnection); + this, +#ifdef USE_GDBSERVER + SLOT(debuggerOutput(QString)) +#else + SLOT(handleRemoteOutput(QString)) +#endif + , Qt::QueuedConnection); } MaemoDebugRunControl::~MaemoDebugRunControl() @@ -406,7 +422,6 @@ MaemoDebugRunControl::~MaemoDebugRunControl() disconnect(SIGNAL(addToOutputWindow(RunControl*,QString, bool))); disconnect(SIGNAL(addToOutputWindowInline(RunControl*,QString, bool))); stop(); - debuggingFinished(); } void MaemoDebugRunControl::startInternal() @@ -422,12 +437,23 @@ QString MaemoDebugRunControl::remoteCall() const .arg(executableFilePathOnTarget()).arg(targetCmdLineSuffix()); } +void MaemoDebugRunControl::startExecution() +{ +#ifdef USE_GDBSERVER + AbstractMaemoRunControl::startExecution(); +#else + startDebugging(); +#endif +} + void MaemoDebugRunControl::handleRemoteOutput(const QString &output) { +#ifdef USE_GDBSERVER if (!m_debuggingStarted) { m_debuggingStarted = true; startDebugging(); } +#endif emit addToOutputWindowInline(this, output, false); } @@ -449,7 +475,11 @@ bool MaemoDebugRunControl::isRunning() const void MaemoDebugRunControl::debuggingFinished() { +#ifdef USE_GDBSERVER AbstractMaemoRunControl::stopRunning(true); +#else + AbstractMaemoRunControl::handleRunThreadFinished(); +#endif } void MaemoDebugRunControl::debuggerOutput(const QString &output) diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h index 7d4170c1d79..12177e2d711 100644 --- a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h +++ b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h @@ -74,7 +74,7 @@ protected: void startDeployment(bool forDebugging); void deploy(); void stopRunning(bool forDebugging); - void startExecution(); + virtual void startExecution(); void handleError(const QString &errString); const QString executableOnHost() const; const QString executableFileName() const; @@ -85,11 +85,13 @@ protected: QString packageFilePath() const; QString executableFilePathOnTarget() const; +protected slots: + void handleRunThreadFinished(); + private slots: virtual void handleRemoteOutput(const QString &output)=0; void handleInitialCleanupFinished(); void handleDeployThreadFinished(); - void handleRunThreadFinished(); void handleFileCopied(); protected: @@ -160,6 +162,7 @@ private slots: private: virtual void startInternal(); virtual void stopInternal(); + virtual void startExecution(); virtual QString remoteCall() const; QString gdbServerPort() const;