diff --git a/src/libs/utils/historycompleter.cpp b/src/libs/utils/historycompleter.cpp index cb7ca4f35793092b1c50bb09ee284cd36c301562..ae508e0ebced1563237d55f6c9946f8cb6a31ec5 100644 --- a/src/libs/utils/historycompleter.cpp +++ b/src/libs/utils/historycompleter.cpp @@ -198,7 +198,7 @@ HistoryCompleter::HistoryCompleter(QObject *parent) // make an assumption to allow pressing of the down // key, before the first model run: // parent is likely the lineedit - QWidget *p = qobject_cast<QWidget*>(parent); + QWidget *p = qobject_cast<QWidget *>(parent); if (p) { p->installEventFilter(d_ptr->model); QString objectName = p->objectName(); @@ -206,6 +206,11 @@ HistoryCompleter::HistoryCompleter(QObject *parent) return; d_ptr->model->list = d_ptr->model->settings->value(objectName).toStringList(); } + + QLineEdit *l = qobject_cast<QLineEdit *>(parent); + if (l && d_ptr->model->list.count()) + l->setText(d_ptr->model->list.at(0)); + setModel(d_ptr->model); HistoryLineDelegate *delegate = new HistoryLineDelegate; HistoryLineView *view = new HistoryLineView(d_ptr, delegate->pixmap.width()); diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index e39691323e0c58d105be1281245a9143a2071fc5..db73a14a820deddd55ad24f7e3d3d141a918daaa 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -106,7 +106,8 @@ FORMS += attachexternaldialog.ui \ dumperoptionpage.ui \ commonoptionspage.ui \ startexternaldialog.ui \ - startremotedialog.ui + startremotedialog.ui \ + startremoteenginedialog.ui RESOURCES += debugger.qrc diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h index b1e217311c6e2e68c9f2d014393d82b424bc7c47..d6c788bc670783946c6280a15638ca9ff65a7d3d 100644 --- a/src/plugins/debugger/debuggerconstants.h +++ b/src/plugins/debugger/debuggerconstants.h @@ -140,7 +140,8 @@ enum DebuggerStartMode AttachTcf, // Attach to a running Target Communication Framework agent AttachCore, // Attach to a core file AttachToRemote, // Start and attach to a remote process - StartRemoteGdb // Start gdb itself remotely + StartRemoteGdb, // Start gdb itself remotely + StartRemoteEngine // Start ipc guest engine on other machine }; enum DebuggerCapabilities diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp index b733476691121a87e3cc179342bc7fd9f6a6a219..b93459fa9fff3c07697a868b6524fa1110c0705e 100644 --- a/src/plugins/debugger/debuggerdialogs.cpp +++ b/src/plugins/debugger/debuggerdialogs.cpp @@ -36,6 +36,7 @@ #include "ui_attachtcfdialog.h" #include "ui_startexternaldialog.h" #include "ui_startremotedialog.h" +#include "ui_startremoteenginedialog.h" #ifdef Q_OS_WIN # include "shared/dbgwinutils.h" @@ -896,5 +897,54 @@ bool AddressDialog::isValid() const return ok; } +/////////////////////////////////////////////////////////////////////// +// +// StartRemoteEngineDialog +// +/////////////////////////////////////////////////////////////////////// + +StartRemoteEngineDialog::StartRemoteEngineDialog(QWidget *parent) : + QDialog(parent) , + m_ui(new Ui::StartRemoteEngineDialog) +{ + m_ui->setupUi(this); + m_ui->host->setCompleter(new HistoryCompleter(m_ui->host)); + m_ui->username->setCompleter(new HistoryCompleter(m_ui->username)); + m_ui->enginepath->setCompleter(new HistoryCompleter(m_ui->enginepath)); + m_ui->inferiorpath->setCompleter(new HistoryCompleter(m_ui->inferiorpath)); + connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); +} + +StartRemoteEngineDialog::~StartRemoteEngineDialog() +{ +} + +QString StartRemoteEngineDialog::host() const +{ + return m_ui->host->text(); +} + +QString StartRemoteEngineDialog::username() const +{ + return m_ui->username->text(); +} + +QString StartRemoteEngineDialog::password() const +{ + return m_ui->password->text(); +} + +QString StartRemoteEngineDialog::inferiorPath() const +{ + return m_ui->inferiorpath->text(); +} + +QString StartRemoteEngineDialog::enginePath() const +{ + return m_ui->enginepath->text(); +} + + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/debuggerdialogs.h b/src/plugins/debugger/debuggerdialogs.h index bf97d1674afa3d224514693a1ad3a76e023a638b..09aa90fd0e23a5f0c4575ccf8244e57e1e953604 100644 --- a/src/plugins/debugger/debuggerdialogs.h +++ b/src/plugins/debugger/debuggerdialogs.h @@ -45,6 +45,7 @@ class AttachExternalDialog; class AttachTcfDialog; class StartExternalDialog; class StartRemoteDialog; +class StartRemoteEngineDialog; } // namespace Ui QT_END_NAMESPACE @@ -242,6 +243,23 @@ private: QDialogButtonBox *m_box; }; +class StartRemoteEngineDialog : public QDialog +{ + Q_OBJECT + +public: + explicit StartRemoteEngineDialog(QWidget *parent); + ~StartRemoteEngineDialog(); + QString username() const; + QString host() const; + QString password() const; + QString enginePath() const; + QString inferiorPath() const; + +private: + Ui::StartRemoteEngineDialog *m_ui; +}; + } // namespace Debugger } // namespace Internal diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 105303e63b17dceb263e1f1346ca08e04b7ff948..7ceb32170a1e9a8cbebf1cda38073bbd148d4fdc 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -345,6 +345,7 @@ const char * const ATTACHCORE = "Debugger.AttachCore"; const char * const ATTACHTCF = "Debugger.AttachTcf"; const char * const ATTACHREMOTE = "Debugger.AttachRemote"; const char * const ATTACHREMOTECDB = "Debugger.AttachRemoteCDB"; +const char * const STARTREMOTELLDB = "Debugger.StartRemoteLLDB"; const char * const DETACH = "Debugger.Detach"; const char * const RUN_TO_LINE1 = "Debugger.RunToLine1"; @@ -1005,6 +1006,7 @@ public slots: void startExternalApplication(); void startRemoteCdbSession(); void startRemoteApplication(); + void startRemoteEngine(); void attachExternalApplication(); void attachExternalApplication (qint64 pid, const QString &binary, const QString &crashParameter); @@ -1307,6 +1309,7 @@ public: QAction *m_startExternalAction; QAction *m_startRemoteAction; QAction *m_startRemoteCdbAction; + QAction *m_startRemoteLldbAction; QAction *m_attachExternalAction; QAction *m_attachCoreAction; QAction *m_attachTcfAction; @@ -1716,6 +1719,10 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, act->setText(tr("Start and Debug External Application...")); connect(act, SIGNAL(triggered()), SLOT(startExternalApplication())); + act = m_startRemoteLldbAction = new QAction(this); + act->setText(tr("Start and Debug External Application with External Engine...")); + connect(act, SIGNAL(triggered()), SLOT(startRemoteEngine())); + act = m_attachExternalAction = new QAction(this); act->setText(tr("Attach to Running External Application...")); connect(act, SIGNAL(triggered()), SLOT(attachExternalApplication())); @@ -1765,6 +1772,11 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, cmd->setAttribute(Command::CA_Hide); mstart->addAction(cmd, CC::G_DEFAULT_ONE); + cmd = am->registerAction(m_startRemoteLldbAction, + Constants::STARTREMOTELLDB, globalcontext); + cmd->setAttribute(Command::CA_Hide); + mstart->addAction(cmd, CC::G_DEFAULT_ONE); + cmd = am->registerAction(m_attachExternalAction, Constants::ATTACHEXTERNAL, globalcontext); cmd->setAttribute(Command::CA_Hide); @@ -2286,6 +2298,32 @@ void DebuggerPluginPrivate::startRemoteApplication() startDebugger(rc); } +void DebuggerPluginPrivate::startRemoteEngine() +{ + DebuggerStartParameters sp; + StartRemoteEngineDialog dlg(mainWindow()); + if (dlg.exec() != QDialog::Accepted) + return; + + sp.connParams.host = dlg.host(); + sp.connParams.uname = dlg.username(); + sp.connParams.pwd = dlg.password(); + + + qDebug() << sp.connParams.host << sp.connParams.uname << sp.connParams.pwd; + + sp.connParams.timeout = 5; + sp.connParams.authType = SshConnectionParameters::AuthByPwd; + sp.connParams.port = 22; + sp.connParams.proxyType = SshConnectionParameters::NoProxy; + + sp.executable = dlg.inferiorPath(); + sp.serverStartScript = dlg.enginePath(); + sp.startMode = StartRemoteEngine; + if (RunControl *rc = createDebugger(sp)) + startDebugger(rc); +} + void DebuggerPluginPrivate::enableReverseDebuggingTriggered(const QVariant &value) { QTC_ASSERT(m_reverseToolButton, return); diff --git a/src/plugins/debugger/debuggerrunner.cpp b/src/plugins/debugger/debuggerrunner.cpp index c0fd349492e2424cfc61ad50dede5eb2b8e23c80..69ca228bd86c5462d271702bbeb6dab2a6b7f43a 100644 --- a/src/plugins/debugger/debuggerrunner.cpp +++ b/src/plugins/debugger/debuggerrunner.cpp @@ -501,11 +501,10 @@ void DebuggerRunControl::createEngine(const DebuggerStartParameters &startParams } } - if (getenv("QTC_LLDB_GUEST")) { + // Fixme: unclean ipc override. Someone please have a better idea + if (sp.startMode == StartRemoteEngine) + // for now thats the only supported ipc engine engineType = LldbEngineType; - sp.executable = sp.processArgs; - qDebug() << "DEBUGGING" << sp.executable; - } // Fixme: 1 of 3 testing hacks. if (sp.processArgs.startsWith(__("@tcf@ "))) diff --git a/src/plugins/debugger/lldb/lldbenginehost.cpp b/src/plugins/debugger/lldb/lldbenginehost.cpp index 49a6ff12692ca44571c67560692b85d412099b52..a5f3b79b356b68d4e5738501a52685fec0c59e9b 100644 --- a/src/plugins/debugger/lldb/lldbenginehost.cpp +++ b/src/plugins/debugger/lldb/lldbenginehost.cpp @@ -58,45 +58,140 @@ namespace Debugger { namespace Internal { +SshIODevice::SshIODevice(Core::SshRemoteProcessRunner::Ptr r) + : runner(r) + , buckethead(0) +{ + setOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered); + connect (runner.data(), SIGNAL(processStarted()), + this, SLOT(processStarted())); + connect(runner.data(), SIGNAL(processOutputAvailable(const QByteArray &)), + this, SLOT(outputAvailable(const QByteArray &))); + connect(runner.data(), SIGNAL(processErrorOutputAvailable(const QByteArray &)), + this, SLOT(errorOutputAvailable(const QByteArray &))); +} +qint64 SshIODevice::bytesAvailable () const +{ + qint64 r = QIODevice::bytesAvailable(); + foreach (const QByteArray &bucket, buckets) + r += bucket.size(); + r-= buckethead; + return r; +} +qint64 SshIODevice::writeData (const char * data, qint64 maxSize) +{ + if (proc == 0) { + startupbuffer += QByteArray::fromRawData(data, maxSize); + return maxSize; + } + proc->sendInput(QByteArray::fromRawData(data, maxSize)); + return maxSize; +} +qint64 SshIODevice::readData (char * data, qint64 maxSize) +{ + if (proc == 0) + return 0; + qint64 size = maxSize; + while (size > 0) { + if (!buckets.size()) { + return maxSize - size; + } + QByteArray &bucket = buckets.head(); + if ((size + buckethead) >= bucket.size()) { + int d = bucket.size() - buckethead; + memcpy(data, bucket.data() + buckethead, d); + data += d; + size -= d; + buckets.dequeue(); + buckethead = 0; + } else { + memcpy(data, bucket.data() + buckethead, size); + data += size; + buckethead += size; + size = 0; + } + } + return maxSize - size; +} + +void SshIODevice::processStarted() +{ + proc = runner->process(); + proc->sendInput(startupbuffer); +} + +void SshIODevice::outputAvailable(const QByteArray &output) +{ + buckets.enqueue(output); + emit readyRead(); +} + +void SshIODevice::errorOutputAvailable(const QByteArray &output) +{ + fprintf(stderr, "%s", output.data()); +} + + LldbEngineHost::LldbEngineHost(const DebuggerStartParameters &startParameters) :IPCEngineHost(startParameters) { showMessage(QLatin1String("setting up coms")); - m_guestProcess = new QProcess(this); + if (startParameters.startMode == StartRemoteEngine) + { + m_guestProcess = 0; + Core::SshRemoteProcessRunner::Ptr runner = + Core::SshRemoteProcessRunner::create(startParameters.connParams); + connect (runner.data(), SIGNAL(connectionError(Core::SshError)), + this, SLOT(sshConnectionError(Core::SshError))); + runner->run(startParameters.serverStartScript.toUtf8()); + setGuestDevice(new SshIODevice(runner)); + } else { + m_guestProcess = new QProcess(this); - connect(m_guestProcess, SIGNAL(finished(int, QProcess::ExitStatus)), - this, SLOT(finished(int, QProcess::ExitStatus))); + connect(m_guestProcess, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(finished(int, QProcess::ExitStatus))); - connect(m_guestProcess, SIGNAL(readyReadStandardError()), this, - SLOT(stderrReady())); + connect(m_guestProcess, SIGNAL(readyReadStandardError()), this, + SLOT(stderrReady())); - QString a = Core::ICore::instance()->resourcePath() + QLatin1String("/qtcreator-lldb"); - if(getenv("QTC_LLDB_GUEST") != 0) - a = QString::fromLocal8Bit(getenv("QTC_LLDB_GUEST")); + QString a = Core::ICore::instance()->resourcePath() + QLatin1String("/qtcreator-lldb"); + if(getenv("QTC_LLDB_GUEST") != 0) + a = QString::fromLocal8Bit(getenv("QTC_LLDB_GUEST")); - showStatusMessage(QString(QLatin1String("starting %1")).arg(a)); + showStatusMessage(QString(QLatin1String("starting %1")).arg(a)); - m_guestProcess->start(a, QStringList()); - m_guestProcess->setReadChannel(QProcess::StandardOutput); + m_guestProcess->start(a, QStringList(), QIODevice::ReadWrite | QIODevice::Unbuffered); + m_guestProcess->setReadChannel(QProcess::StandardOutput); - if (!m_guestProcess->waitForStarted()) { - showStatusMessage(tr("qtcreator-lldb failed to start %1").arg(m_guestProcess->error())); - notifyEngineSpontaneousShutdown(); - return; - } + if (!m_guestProcess->waitForStarted()) { + showStatusMessage(tr("qtcreator-lldb failed to start %1").arg(m_guestProcess->error())); + notifyEngineSpontaneousShutdown(); + return; + } - setGuestDevice(m_guestProcess); + setGuestDevice(m_guestProcess); + } } LldbEngineHost::~LldbEngineHost() { showMessage(QLatin1String("tear down qtcreator-lldb")); - disconnect(m_guestProcess, SIGNAL(finished(int, QProcess::ExitStatus)), - this, SLOT(finished (int, QProcess::ExitStatus))); - m_guestProcess->terminate(); - m_guestProcess->kill(); + + if (m_guestProcess) { + disconnect(m_guestProcess, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(finished (int, QProcess::ExitStatus))); + + + m_guestProcess->terminate(); + m_guestProcess->kill(); + } + if (m_ssh.data() && m_ssh->process().data()) { + // TODO: openssh doesn't do that + + m_ssh->process()->kill(); + } } void LldbEngineHost::nuke() @@ -108,6 +203,10 @@ void LldbEngineHost::nuke() m_guestProcess->kill(); notifyEngineSpontaneousShutdown(); } +void LldbEngineHost::sshConnectionError(Core::SshError e) +{ + showStatusMessage(tr("ssh connection error: %1").arg(e)); +} void LldbEngineHost::finished(int, QProcess::ExitStatus status) { @@ -128,3 +227,4 @@ DebuggerEngine *createLldbEngine(const DebuggerStartParameters &startParameters) } // namespace Internal } // namespace Debugger + diff --git a/src/plugins/debugger/lldb/lldbenginehost.h b/src/plugins/debugger/lldb/lldbenginehost.h index 2e5c18580d59473d4bb560ffafcbda6d8f54f928..2e17370d2922d3d6142e8a92afde288329e26dde 100644 --- a/src/plugins/debugger/lldb/lldbenginehost.h +++ b/src/plugins/debugger/lldb/lldbenginehost.h @@ -31,12 +31,37 @@ #define DEBUGGER_LLDBENGINE_HOST_H #include "ipcenginehost.h" +#include <coreplugin/ssh/ssherrors.h> +#include <coreplugin/ssh/sshconnection.h> +#include <coreplugin/ssh/sshremoteprocess.h> +#include <coreplugin/ssh/sshremoteprocessrunner.h> #include <QtCore/QProcess> +#include <QtCore/QQueue> namespace Debugger { namespace Internal { +class SshIODevice : public QIODevice +{ +Q_OBJECT +public: + SshIODevice(Core::SshRemoteProcessRunner::Ptr r); + virtual qint64 bytesAvailable () const; + virtual qint64 writeData (const char * data, qint64 maxSize); + virtual qint64 readData (char * data, qint64 maxSize); +private slots: + void processStarted(); + void outputAvailable(const QByteArray &output); + void errorOutputAvailable(const QByteArray &output); +private: + Core::SshRemoteProcessRunner::Ptr runner; + Core::SshRemoteProcess::Ptr proc; + int buckethead; + QQueue<QByteArray> buckets; + QByteArray startupbuffer; +}; + class LldbEngineHost : public IPCEngineHost { Q_OBJECT @@ -47,9 +72,11 @@ public: private: QProcess *m_guestProcess; + Core::SshRemoteProcessRunner::Ptr m_ssh; protected: void nuke(); private slots: + void sshConnectionError(Core::SshError); void finished(int, QProcess::ExitStatus); void stderrReady(); };