OSDN Git Service

lldb: introduce dialog to start engine remotely
authorArvid Ephraim Picciani <arvid.picciani@nokia.com>
Wed, 1 Dec 2010 14:41:08 +0000 (15:41 +0100)
committerArvid Ephraim Picciani <arvid.picciani@nokia.com>
Wed, 1 Dec 2010 14:50:18 +0000 (15:50 +0100)
src/libs/utils/historycompleter.cpp
src/plugins/debugger/debugger.pro
src/plugins/debugger/debuggerconstants.h
src/plugins/debugger/debuggerdialogs.cpp
src/plugins/debugger/debuggerdialogs.h
src/plugins/debugger/debuggerplugin.cpp
src/plugins/debugger/debuggerrunner.cpp
src/plugins/debugger/lldb/lldbenginehost.cpp
src/plugins/debugger/lldb/lldbenginehost.h

index cb7ca4f..ae508e0 100644 (file)
@@ -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());
index e396913..db73a14 100644 (file)
@@ -106,7 +106,8 @@ FORMS += attachexternaldialog.ui \
     dumperoptionpage.ui \
     commonoptionspage.ui \
     startexternaldialog.ui \
-    startremotedialog.ui
+    startremotedialog.ui \
+    startremoteenginedialog.ui
 
 RESOURCES += debugger.qrc
 
index b1e2173..d6c788b 100644 (file)
@@ -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
index b733476..b93459f 100644 (file)
@@ -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
index bf97d16..09aa90f 100644 (file)
@@ -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
 
index 105303e..7ceb321 100644 (file)
@@ -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);
index c0fd349..69ca228 100644 (file)
@@ -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@ ")))
index 49a6ff1..a5f3b79 100644 (file)
 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
+
index 2e5c185..2e17370 100644 (file)
 #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();
 };