From 1b9b962b2c580d72e5ad42672dcad609bc5c9e96 Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Fri, 19 Nov 2010 16:13:22 +0100
Subject: [PATCH] Debugger[new CDB]: Polish options, add remote debugging.

---
 src/libs/qtcreatorcdbext/eventcallback.cpp    |   8 +-
 src/libs/qtcreatorcdbext/extensioncontext.h   |   3 +-
 src/libs/qtcreatorcdbext/qtcreatorcdbext.def  |   1 +
 .../qtcreatorcdbext/qtcreatorcdbextension.cpp |   9 ++
 src/plugins/debugger/cdb2/cdbengine2.cpp      |  61 ++++++++--
 src/plugins/debugger/cdb2/cdbengine2.h        |   3 +-
 src/plugins/debugger/cdb2/cdboptionspage2.cpp | 113 ++++++++++++++++--
 src/plugins/debugger/cdb2/cdboptionspage2.h   |  18 ++-
 .../debugger/cdb2/cdboptionspagewidget2.ui    |  18 ++-
 src/plugins/debugger/cdb2/cdbparsehelpers.cpp |   8 +-
 src/plugins/debugger/debuggerdialogs.cpp      |  95 +++++++++++++++
 src/plugins/debugger/debuggerdialogs.h        |  21 ++++
 src/plugins/debugger/debuggerplugin.cpp       |  38 +++++-
 13 files changed, 360 insertions(+), 36 deletions(-)

diff --git a/src/libs/qtcreatorcdbext/eventcallback.cpp b/src/libs/qtcreatorcdbext/eventcallback.cpp
index f05f66d2adc..285e40caa98 100644
--- a/src/libs/qtcreatorcdbext/eventcallback.cpp
+++ b/src/libs/qtcreatorcdbext/eventcallback.cpp
@@ -221,9 +221,11 @@ STDMETHODIMP EventCallback::ExitProcess(
 {
     ExtensionContext::instance().report('E', 0, eventContextC, "Process exited (%lu)",
                                         ExitCode);
-
-    dprintf("%s ExitProcess %u\n", creatorOutputPrefixC, ExitCode);
-    return m_wrapped ? m_wrapped->ExitProcess(ExitCode) : S_OK;
+    const HRESULT hr = m_wrapped ? m_wrapped->ExitProcess(ExitCode) : S_OK;
+    // Remotely debugged process exited, there is no session-inactive notification.
+    // Note: We get deleted here, so, order is important.
+    ExtensionContext::instance().unhookCallbacks();
+    return hr;
 }
 
 STDMETHODIMP EventCallback::LoadModule(
diff --git a/src/libs/qtcreatorcdbext/extensioncontext.h b/src/libs/qtcreatorcdbext/extensioncontext.h
index 79933dffece..a299f9dde31 100644
--- a/src/libs/qtcreatorcdbext/extensioncontext.h
+++ b/src/libs/qtcreatorcdbext/extensioncontext.h
@@ -61,6 +61,8 @@ public:
     // Call this from the first extension command that gets a client.
     // Does not work when called from initialization.
     void hookCallbacks(CIDebugClient *client);
+    // Undo hooking.
+    void unhookCallbacks();
 
     // Report output in standardized format understood by Qt Creator.
     // '<qtcreatorcdbext>|R|<token>|<serviceName>|<one-line-output>'.
@@ -81,7 +83,6 @@ public:
     void setStopReason(const StopReasonMap &, const std::string &reason = std::string());
 
 private:
-    void unhookCallbacks();
     bool isInitialized() const;
     void discardSymbolGroup();
 
diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def
index e7b7b62aaeb..6f71c02b344 100644
--- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def
+++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def
@@ -12,4 +12,5 @@ modules
 idle
 help
 memory
+shutdownex
 KnownStructOutput
diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
index 8212b32620e..dce4db70358 100644
--- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
+++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
@@ -351,6 +351,15 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
     return S_OK;
 }
 
+// Extension command 'shutdownex' (shutdown is reserved):
+// Unhook the output callbacks. This is normally done by the session
+// inaccessible notification, however, this does not work for remote-controlled sessions.
+extern "C" HRESULT CALLBACK shutdownex(CIDebugClient *, PCSTR)
+{
+    ExtensionContext::instance().unhookCallbacks();
+    return S_OK;
+}
+
 // Hook for dumping Known Structs. Not currently used.
 // Shows up in 'dv' as well as IDebugSymbolGroup::GetValueText.
 
diff --git a/src/plugins/debugger/cdb2/cdbengine2.cpp b/src/plugins/debugger/cdb2/cdbengine2.cpp
index 32ab4085e2d..86d8a0e5e5b 100644
--- a/src/plugins/debugger/cdb2/cdbengine2.cpp
+++ b/src/plugins/debugger/cdb2/cdbengine2.cpp
@@ -48,6 +48,7 @@
 
 #include <coreplugin/icore.h>
 
+#include <utils/synchronousprocess.h>
 #include <utils/winutils.h>
 #include <utils/qtcassert.h>
 #include <utils/savedaction.h>
@@ -228,7 +229,6 @@ static inline bool validMode(DebuggerStartMode sm)
     case NoStartMode:
     case AttachTcf:
     case AttachCore:
-    case AttachToRemote:
     case StartRemoteGdb:
         return false;
     default:
@@ -334,7 +334,7 @@ void CdbEngine::setupEngine()
 }
 
 // Determine full path to the CDB extension library.
-static inline QString extensionLibraryName(bool is64Bit)
+QString CdbEngine::extensionLibraryName(bool is64Bit)
 {
     // Determine extension lib name and path to use
     QString rc;
@@ -387,18 +387,24 @@ bool CdbEngine::doSetupEngine(QString *errorMessage)
     // Determine extension lib name and path to use
     // The extension is passed as relative name with the path variable set
     //(does not work with absolute path names)
-    const QFileInfo extensionFi(extensionLibraryName(m_options->is64bit));
+    const QFileInfo extensionFi(CdbEngine::extensionLibraryName(m_options->is64bit));
     if (!extensionFi.isFile()) {
         *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
                 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
         return false;
     }
+    const QString extensionFileName = extensionFi.fileName();
     // Prepare arguments
     const DebuggerStartParameters &sp = startParameters();
     QStringList arguments;
+    const bool isRemote = sp.startMode == AttachToRemote;
+    if (isRemote) { // Must be first
+        arguments << QLatin1String("-remote") << sp.remoteChannel;
+    } else {
+        arguments << (QLatin1String("-a") + extensionFileName);
+    }
     // Source line info/No terminal breakpoint / Pull extension
     arguments << QLatin1String("-lines") << QLatin1String("-G")
-              << (QLatin1String("-a") + extensionFi.fileName())
     // register idle (debuggee stop) notification
               << QLatin1String("-c")
               << QString::fromAscii(".idle_cmd " + m_extensionCommandPrefixBA + "idle");
@@ -413,8 +419,10 @@ bool CdbEngine::doSetupEngine(QString *errorMessage)
     case StartExternal:
         arguments << QDir::toNativeSeparators(sp.executable);
         break;
+    case AttachToRemote:
+        break;
     case AttachExternal:
-    case AttachCrashedExternal:   // @TODO: event handle for crashed?
+    case AttachCrashedExternal:
         arguments << QLatin1String("-p") << QString::number(sp.attachPID);
         if (sp.startMode == AttachCrashedExternal)
             arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
@@ -451,17 +459,28 @@ bool CdbEngine::doSetupEngine(QString *errorMessage)
     showMessage(QString::fromLatin1("%1 running as %2").
                 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
     m_hasDebuggee = true;
+    if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
+        m_accessible = true;
+        const QByteArray loadCommand = QByteArray(".load ")
+                + extensionFileName.toLocal8Bit();
+        postCommand(loadCommand, 0);
+        notifyEngineSetupOk();
+    }
     return true;
 }
 
 void CdbEngine::setupInferior()
 {
+    if (debug)
+        qDebug("setupInferior");
     attemptBreakpointSynchronization();
     postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
 }
 
 void CdbEngine::runEngine()
 {
+    if (debug)
+        qDebug("runEngine");
     postCommand("g", 0);
 }
 
@@ -477,6 +496,11 @@ void CdbEngine::shutdownInferior()
         notifyInferiorShutdownOk();
         return;
     }
+    if (!canInterruptInferior()) {
+        notifyInferiorShutdownFailed();
+        return;
+    }
+
     if (m_accessible) {
         if (startParameters().startMode == AttachExternal || startParameters().startMode == AttachCrashedExternal)
             detachDebugger();
@@ -513,8 +537,19 @@ void CdbEngine::shutdownEngine()
     if (m_accessible) {
         if (startParameters().startMode == AttachExternal)
             detachDebugger();
-        postCommand("q", 0);
+        // Remote requires a bit more force to quit.
+        if (startParameters().startMode == AttachToRemote) {
+            postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
+            postCommand("qq", 0);
+        } else {
+            postCommand("q", 0);
+        }
+        m_notifyEngineShutdownOnTermination = true;
+        return;
+    } else {
+        // Remote process. No can do, currently
         m_notifyEngineShutdownOnTermination = true;
+        Utils::SynchronousProcess::stopProcess(m_process);
         return;
     }
     // Lost debuggee, debugger should quit anytime now
@@ -638,9 +673,21 @@ void CdbEngine::doContinueInferior()
     postCommand(QByteArray("g"), 0);
 }
 
+bool CdbEngine::canInterruptInferior() const
+{
+    return startParameters().startMode != AttachToRemote;
+}
+
 void CdbEngine::interruptInferior()
 {
-    doInterruptInferior(NoSpecialStop);
+    if (canInterruptInferior()) {
+        doInterruptInferior(NoSpecialStop);
+    } else {
+        showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
+        notifyInferiorStopOk();
+        notifyInferiorRunRequested();
+        notifyInferiorRunOk();
+    }
 }
 
 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
diff --git a/src/plugins/debugger/cdb2/cdbengine2.h b/src/plugins/debugger/cdb2/cdbengine2.h
index 0737ce58d05..dd4e8e7f279 100644
--- a/src/plugins/debugger/cdb2/cdbengine2.h
+++ b/src/plugins/debugger/cdb2/cdbengine2.h
@@ -115,7 +115,7 @@ public:
     virtual void reloadSourceFiles();
     virtual void reloadFullStack();
 
-    //virtual bool isSynchronous() const { return true; }
+    static QString extensionLibraryName(bool is64Bit);
 
 private slots:
     void readyReadStandardOut();
@@ -151,6 +151,7 @@ private:
     void doContinueInferior();
     inline void parseOutputLine(QByteArray line);
     inline bool isCdbProcessRunning() const { return m_process.state() != QProcess::NotRunning; }
+    bool canInterruptInferior() const;
 
     // Builtin commands
     void dummyHandler(const CdbBuiltinCommandPtr &);
diff --git a/src/plugins/debugger/cdb2/cdboptionspage2.cpp b/src/plugins/debugger/cdb2/cdboptionspage2.cpp
index c8a67eb7d6c..fd0f3b58002 100644
--- a/src/plugins/debugger/cdb2/cdboptionspage2.cpp
+++ b/src/plugins/debugger/cdb2/cdboptionspage2.cpp
@@ -30,15 +30,23 @@
 #include "cdboptionspage2.h"
 #include "cdboptions2.h"
 #include "debuggerconstants.h"
+#include "cdbengine2.h"
 
 #ifdef Q_OS_WIN
 #    include <utils/winutils.h>
 #endif
+#include <utils/synchronousprocess.h>
+
 #include <coreplugin/icore.h>
 
 #include <QtCore/QCoreApplication>
 #include <QtCore/QUrl>
+#include <QtCore/QFileInfo>
+#include <QtCore/QDir>
+#include <QtCore/QDateTime>
 #include <QtCore/QTextStream>
+#include <QtCore/QTimer>
+#include <QtCore/QProcess>
 #include <QtGui/QMessageBox>
 #include <QtGui/QDesktopServices>
 
@@ -66,7 +74,7 @@ static inline QString msgPathConfigNote()
 }
 
 CdbOptionsPageWidget::CdbOptionsPageWidget(QWidget *parent) :
-    QWidget(parent)
+    QWidget(parent), m_reportTimer(0)
 {
     m_ui.setupUi(this);
     m_ui.noteLabel->setText(msgPathConfigNote());
@@ -75,10 +83,9 @@ CdbOptionsPageWidget::CdbOptionsPageWidget(QWidget *parent) :
 
     m_ui.pathChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
     m_ui.pathChooser->addButton(tr("Autodetect"), this, SLOT(autoDetect()));
-    m_ui.failureLabel->setVisible(false);
+    m_ui.cdbPathGroupBox->installEventFilter(this);
 }
 
-
 void CdbOptionsPageWidget::setOptions(CdbOptions &o)
 {
     m_ui.pathChooser->setPath(o.executable);
@@ -88,17 +95,33 @@ void CdbOptionsPageWidget::setOptions(CdbOptions &o)
     m_ui.sourcePathListEditor->setPathList(o.sourcePaths);
 }
 
+bool CdbOptionsPageWidget::is64Bit() const
+{
+    return m_ui.is64BitCheckBox->isChecked();
+}
+
+QString CdbOptionsPageWidget::path() const
+{
+    return m_ui.pathChooser->path();
+}
+
 CdbOptions CdbOptionsPageWidget::options() const
 {
     CdbOptions  rc;
-    rc.executable = m_ui.pathChooser->path();
+    rc.executable = path();
     rc.enabled = m_ui.cdbPathGroupBox->isChecked();
-    rc.is64bit = m_ui.is64BitCheckBox->isChecked();
+    rc.is64bit = is64Bit();
     rc.symbolPaths = m_ui.symbolPathListEditor->pathList();
     rc.sourcePaths = m_ui.sourcePathListEditor->pathList();
     return rc;
 }
 
+void CdbOptionsPageWidget::hideReportLabel()
+{
+    m_ui.reportLabel->clear();
+    m_ui.reportLabel->setVisible(false);
+}
+
 void CdbOptionsPageWidget::autoDetect()
 {
     QString executable;
@@ -109,6 +132,10 @@ void CdbOptionsPageWidget::autoDetect()
     if (ok) {
         m_ui.is64BitCheckBox->setChecked(is64bit);
         m_ui.pathChooser->setPath(executable);
+        QString report;
+        // Now check for the extension library as well.
+        const bool allOk = checkInstallation(executable, is64Bit(), &report);
+        setReport(report, allOk);
     } else {
         const QString msg = tr("\"Debugging Tools for Windows\" could not be found.");
         const QString details = tr("Checked:\n%1").arg(checkedDirectories.join(QString(QLatin1Char('\n'))));
@@ -118,10 +145,23 @@ void CdbOptionsPageWidget::autoDetect()
     }
 }
 
-void CdbOptionsPageWidget::setFailureMessage(const QString &msg)
+void CdbOptionsPageWidget::setReport(const QString &msg, bool success)
 {
-    m_ui.failureLabel->setText(msg);
-    m_ui.failureLabel->setVisible(!msg.isEmpty());
+    // Hide label after some interval
+    if (!m_reportTimer) {
+        m_reportTimer = new QTimer(this);
+        m_reportTimer->setSingleShot(true);
+        connect(m_reportTimer, SIGNAL(timeout()), this, SLOT(hideReportLabel()));
+    } else {
+        if (m_reportTimer->isActive())
+            m_reportTimer->stop();
+    }
+    m_reportTimer->setInterval(success ? 10000 : 20000);
+    m_reportTimer->start();
+
+    m_ui.reportLabel->setText(msg);
+    m_ui.reportLabel->setStyleSheet(success ? QString() : QString::fromAscii("background-color : 'red'"));
+    m_ui.reportLabel->setVisible(true);
 }
 
 void CdbOptionsPageWidget::downLoadLinkActivated(const QString &link)
@@ -138,6 +178,62 @@ QString CdbOptionsPageWidget::searchKeywords() const
     return rc;
 }
 
+static QString cdbVersion(const QString &executable)
+{
+    QProcess cdb;
+    cdb.start(executable, QStringList(QLatin1String("-version")));
+    cdb.closeWriteChannel();
+    if (!cdb.waitForStarted())
+        return QString();
+    if (!cdb.waitForFinished()) {
+        Utils::SynchronousProcess::stopProcess(cdb);
+        return QString();
+    }
+    return QString::fromLocal8Bit(cdb.readAllStandardOutput());
+}
+
+bool CdbOptionsPageWidget::checkInstallation(const QString &executable,
+                                             bool is64Bit, QString *message)
+{
+    // 1) Check on executable
+    unsigned checkedItems = 0;
+    QString rc;
+    if (executable.isEmpty()) {
+        message->append(tr("No cdb executable specified.\n"));
+    } else {
+        const QString version = cdbVersion(executable);
+        if (version.isEmpty()) {
+            message->append(tr("Unable to determine version of %1.\n").
+                            arg(executable));
+        } else {
+            message->append(tr("Version: %1").arg(version));
+            checkedItems++;
+        }
+    }
+
+    // 2) Check on extension library
+    const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64Bit));
+    if (extensionFi.isFile()) {
+        message->append(tr("Extension library: %1, built: %3.\n").
+                        arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath())).
+                        arg(extensionFi.lastModified().toString(Qt::SystemLocaleShortDate)));
+        checkedItems++;
+    } else {
+        message->append("Extension library not found.\n");
+    }
+    return checkedItems == 2u;
+}
+
+bool CdbOptionsPageWidget::eventFilter(QObject *o, QEvent *e)
+{
+    if (o != m_ui.cdbPathGroupBox || e->type() != QEvent::ToolTip)
+        return QWidget::eventFilter(o, e);
+    QString message;
+    checkInstallation(path(), is64Bit(), &message);
+    m_ui.cdbPathGroupBox->setToolTip(message);
+    return false;
+}
+
 // ---------- CdbOptionsPage
 
 CdbOptionsPage *CdbOptionsPage::m_instance = 0;
@@ -183,7 +279,6 @@ QWidget *CdbOptionsPage::createPage(QWidget *parent)
 {
     m_widget = new CdbOptionsPageWidget(parent);
     m_widget->setOptions(*m_options);
-    m_widget->setFailureMessage(m_failureMessage);
     if (m_searchKeywords.isEmpty())
         m_searchKeywords = m_widget->searchKeywords();
     return m_widget;
diff --git a/src/plugins/debugger/cdb2/cdboptionspage2.h b/src/plugins/debugger/cdb2/cdboptionspage2.h
index 11e5183a2fa..7f67f123b27 100644
--- a/src/plugins/debugger/cdb2/cdboptionspage2.h
+++ b/src/plugins/debugger/cdb2/cdboptionspage2.h
@@ -39,6 +39,8 @@
 #include <QtCore/QPointer>
 #include <QtCore/QSharedPointer>
 
+QT_FORWARD_DECLARE_CLASS(QTimer)
+
 namespace Debugger {
 namespace Cdb {
 
@@ -51,16 +53,25 @@ public:
     void setOptions(CdbOptions &o);
     CdbOptions options() const;
 
-    void setFailureMessage(const QString &);
-
     QString searchKeywords() const;
 
+    virtual bool eventFilter(QObject *, QEvent *);
+
 private slots:
     void autoDetect();
     void downLoadLinkActivated(const QString &);
+    void hideReportLabel();
 
 private:
+    void setReport(const QString &, bool success);
+    inline bool is64Bit() const;
+    inline QString path() const;
+
+    static bool checkInstallation(const QString &executable, bool is64Bit,
+                                  QString *message);
+
     Ui::CdbOptionsPageWidget2 m_ui;
+    QTimer *m_reportTimer;
 };
 
 class CdbOptionsPage : public Core::IOptionsPage
@@ -87,15 +98,12 @@ public:
 
     static QString settingsId();
 
-    // Load  failure messages can be displayed here
-    void setFailureMessage(const QString &msg) { m_failureMessage = msg; }
     QSharedPointer<CdbOptions> options() const { return m_options; }
 
 private:
     static CdbOptionsPage *m_instance;
     const QSharedPointer<CdbOptions> m_options;
     QPointer<CdbOptionsPageWidget> m_widget;
-    QString m_failureMessage;
     QString m_searchKeywords;
 };
 
diff --git a/src/plugins/debugger/cdb2/cdboptionspagewidget2.ui b/src/plugins/debugger/cdb2/cdboptionspagewidget2.ui
index 49d065b569d..9904bb5a3e8 100644
--- a/src/plugins/debugger/cdb2/cdboptionspagewidget2.ui
+++ b/src/plugins/debugger/cdb2/cdboptionspagewidget2.ui
@@ -2,14 +2,19 @@
 <ui version="4.0">
  <class>Debugger::Cdb::CdbOptionsPageWidget2</class>
  <widget class="QWidget" name="Debugger::Cdb::CdbOptionsPageWidget2">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>135</width>
+    <height>246</height>
+   </rect>
+  </property>
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <widget class="QGroupBox" name="cdbPathGroupBox">
-       <property name="toolTip">
-        <string>These options take effect at the next start of Qt Creator.</string>
-       </property>
        <property name="title">
         <string extracomment="Placeholder">CDB</string>
        </property>
@@ -92,13 +97,16 @@
     </spacer>
    </item>
    <item>
-    <widget class="QLabel" name="failureLabel">
+    <widget class="QLabel" name="reportLabel">
      <property name="styleSheet">
-      <string notr="true">background-color: 'red';</string>
+      <string notr="true"/>
      </property>
      <property name="wordWrap">
       <bool>true</bool>
      </property>
+     <property name="textInteractionFlags">
+      <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+     </property>
     </widget>
    </item>
   </layout>
diff --git a/src/plugins/debugger/cdb2/cdbparsehelpers.cpp b/src/plugins/debugger/cdb2/cdbparsehelpers.cpp
index 8debc517947..14da284c210 100644
--- a/src/plugins/debugger/cdb2/cdbparsehelpers.cpp
+++ b/src/plugins/debugger/cdb2/cdbparsehelpers.cpp
@@ -154,10 +154,14 @@ QVariant cdbIntegerValue(const QByteArray &t)
     return converted;
 }
 
-/* Parse:
+/* Parse: 64bit:
 \code
 Child-SP          RetAddr           Call Site
 00000000`0012a290 00000000`70deb844 QtCored4!QString::QString+0x18 [c:\qt\src\corelib\tools\qstring.h @ 729]
+\endcode 32bit:
+\code
+ChildEBP RetAddr
+0012cc68 6714d114 QtCored4!QString::QString+0xf [d:\dev\qt4.7-vs8\qt\src\corelib\tools\qstring.h @ 729]
 \endcode */
 
 static inline bool isHexDigit(char c)
@@ -168,7 +172,7 @@ static inline bool isHexDigit(char c)
 static inline bool parseStackFrame(QByteArray line, Debugger::Internal::StackFrame *frame)
 {
     frame->clear();
-    if (line.isEmpty() || line.startsWith("Child-SP") || !isHexDigit(line.at(0)))
+    if (line.isEmpty() || line.startsWith("Child") || !isHexDigit(line.at(0)))
         return false;
     if (line.endsWith(']')) {
         const int sourceFilePos = line.lastIndexOf('[');
diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp
index 7898dad6216..b7334766911 100644
--- a/src/plugins/debugger/debuggerdialogs.cpp
+++ b/src/plugins/debugger/debuggerdialogs.cpp
@@ -29,6 +29,7 @@
 
 #include "debuggerdialogs.h"
 #include "debuggerconstants.h"
+#include "cdb2/cdbengine2.h"
 
 #include "ui_attachcoredialog.h"
 #include "ui_attachexternaldialog.h"
@@ -43,9 +44,11 @@
 #include <coreplugin/icore.h>
 #include <utils/synchronousprocess.h>
 #include <utils/historycompleter.h>
+#include <utils/qtcassert.h>
 
 #include <QtCore/QDebug>
 #include <QtCore/QProcess>
+#include <QtCore/QRegExp>
 #include <QtCore/QDir>
 #include <QtCore/QFile>
 #include <QtCore/QCoreApplication>
@@ -56,6 +59,7 @@
 #include <QtGui/QProxyModel>
 #include <QtGui/QSortFilterProxyModel>
 #include <QtGui/QMessageBox>
+#include <QtGui/QGroupBox>
 
 using namespace Utils;
 
@@ -744,6 +748,97 @@ void StartRemoteDialog::updateState()
     m_ui->serverStartScript->setEnabled(enabled);
 }
 
+// --------- StartRemoteCdbDialog
+static inline QString cdbRemoteHelp()
+{
+    const char *cdbConnectionSyntax =
+            "Server:Port<br>"
+            "tcp:server=Server,port=Port[,password=Password][,ipversion=6]\n"
+            "tcp:clicon=Server,port=Port[,password=Password][,ipversion=6]\n"
+            "npipe:server=Server,pipe=PipeName[,password=Password]\n"
+            "com:port=COMPort,baud=BaudRate,channel=COMChannel[,password=Password]\n"
+            "spipe:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,pipe=PipeName[,password=Password]\n"
+            "ssl:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,port=Socket[,password=Password]\n"
+            "ssl:proto=Protocol,{certuser=Cert|machuser=Cert},clicon=Server,port=Socket[,password=Password]";
+
+    const QString ext32 = QDir::toNativeSeparators(Debugger::Cdb::CdbEngine::extensionLibraryName(false));
+    const QString ext64 = QDir::toNativeSeparators(Debugger::Cdb::CdbEngine::extensionLibraryName(true));
+    return  StartRemoteCdbDialog::tr(
+                "<html><body><p>The remote CDB needs to load the matching Qt Creator CDB extension "
+                "(<code>%1</code> or <code>%2</code>, respectively).</p><p>Copy it onto the remote machine and set the "
+                "environment variable <code>%3</code> to point to its folder.</p><p>"
+                "Launch the remote CDB as <code>%4 &lt;executable&gt;</code> "
+                " to use TCP/IP as communication protocol.</p><p>Enter the connection parameters as:</p>"
+                "<pre>%5</pre></body></html>").
+            arg(ext32, ext64, QLatin1String("_NT_DEBUGGER_EXTENSION_PATH"),
+                QLatin1String("cdb.exe -server tcp:port=1234"),
+                QLatin1String(cdbConnectionSyntax));
+}
+
+StartRemoteCdbDialog::StartRemoteCdbDialog(QWidget *parent) :
+    QDialog(parent), m_okButton(0), m_lineEdit(new QLineEdit)
+{
+    setWindowTitle(tr("Start a CDB Remote Session"));
+    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+
+    QGroupBox *groupBox = new QGroupBox;
+    QFormLayout *formLayout = new QFormLayout;
+    QLabel *helpLabel = new QLabel(cdbRemoteHelp());
+    helpLabel->setWordWrap(true);
+    helpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
+    formLayout->addRow(helpLabel);
+    QLabel *label = new QLabel(tr("&Connection:"));
+    label->setBuddy(m_lineEdit);
+    m_lineEdit->setMinimumWidth(400);
+    connect(m_lineEdit, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)));
+    formLayout->addRow(label, m_lineEdit);
+    groupBox->setLayout(formLayout);
+
+    QVBoxLayout *vLayout = new QVBoxLayout;
+    vLayout->addWidget(groupBox);
+    QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
+    vLayout->addWidget(box);
+    m_okButton = box->button(QDialogButtonBox::Ok);
+    connect(m_lineEdit, SIGNAL(returnPressed()), m_okButton, SLOT(animateClick()));
+    m_okButton->setEnabled(false);
+    connect(box, SIGNAL(accepted()), this, SLOT(accept()));
+    connect(box, SIGNAL(rejected()), this, SLOT(reject()));
+
+    setLayout(vLayout);
+}
+
+void StartRemoteCdbDialog::accept()
+{
+    if (!m_lineEdit->text().isEmpty())
+        QDialog::accept();
+}
+
+StartRemoteCdbDialog::~StartRemoteCdbDialog()
+{
+}
+
+void StartRemoteCdbDialog::textChanged(const QString &t)
+{
+    m_okButton->setEnabled(!t.isEmpty());
+}
+
+QString StartRemoteCdbDialog::connection() const
+{
+    const QString rc = m_lineEdit->text();
+    // Transform an IP:POrt ('localhost:1234') specification into full spec
+    QRegExp ipRegexp(QLatin1String("([\\w\\.\\-_]+):([0-9]{1,4})"));
+    QTC_ASSERT(ipRegexp.isValid(), return QString());
+    if (ipRegexp.exactMatch(rc))
+        return QString::fromAscii("tcp:server=%1,port=%2").arg(ipRegexp.cap(1), ipRegexp.cap(2));
+    return rc;
+}
+
+void StartRemoteCdbDialog::setConnection(const QString &c)
+{
+    m_lineEdit->setText(c);
+    m_okButton->setEnabled(!c.isEmpty());
+}
+
 AddressDialog::AddressDialog(QWidget *parent) :
         QDialog(parent),
         m_lineEdit(new QLineEdit),
diff --git a/src/plugins/debugger/debuggerdialogs.h b/src/plugins/debugger/debuggerdialogs.h
index 4d168a34563..bf97d1674af 100644
--- a/src/plugins/debugger/debuggerdialogs.h
+++ b/src/plugins/debugger/debuggerdialogs.h
@@ -200,6 +200,27 @@ private:
     Ui::StartRemoteDialog *m_ui;
 };
 
+class StartRemoteCdbDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit StartRemoteCdbDialog(QWidget *parent);
+    ~StartRemoteCdbDialog();
+
+    QString connection() const;
+    void setConnection(const QString &);
+
+    virtual void accept();
+
+private slots:
+    void textChanged(const QString &);
+
+private:
+    QPushButton *m_okButton;
+    QLineEdit *m_lineEdit;
+};
+
 class AddressDialog : public QDialog {
     Q_OBJECT
 public:
diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp
index 00fb6463d18..251e97aa20e 100644
--- a/src/plugins/debugger/debuggerplugin.cpp
+++ b/src/plugins/debugger/debuggerplugin.cpp
@@ -343,6 +343,7 @@ const char * const ATTACHEXTERNAL       = "Debugger.AttachExternal";
 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 DETACH               = "Debugger.Detach";
 
 const char * const RUN_TO_LINE1         = "Debugger.RunToLine1";
@@ -971,6 +972,7 @@ public slots:
 
     void debugProject();
     void startExternalApplication();
+    void startRemoteCdbSession();
     void startRemoteApplication();
     void attachExternalApplication();
     void attachExternalApplication
@@ -1243,6 +1245,7 @@ public:
     QAction *m_debugAction;
     QAction *m_startExternalAction;
     QAction *m_startRemoteAction;
+    QAction *m_startRemoteCdbAction;
     QAction *m_attachExternalAction;
     QAction *m_attachCoreAction;
     QAction *m_attachTcfAction;
@@ -1303,8 +1306,7 @@ public:
     bool m_gdbBinariesChanged;
 };
 
-
-DebuggerPluginPrivate::DebuggerPluginPrivate(DebuggerPlugin *plugin)
+DebuggerPluginPrivate::DebuggerPluginPrivate(DebuggerPlugin *plugin) : m_startRemoteCdbAction(0)
 {
     QTC_ASSERT(!theDebuggerCore, /**/);
     theDebuggerCore = this;
@@ -1669,11 +1671,15 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
     act->setText(tr("Start and Attach to Remote Application..."));
     connect(act, SIGNAL(triggered()), SLOT(startRemoteApplication()));
 
+#ifdef Q_OS_WIN
+    m_startRemoteCdbAction = new QAction(tr("Attach to Remote CDB Session..."), this);
+    connect(m_startRemoteCdbAction, SIGNAL(triggered()), SLOT(startRemoteCdbSession()));
+#endif
+
     act = m_detachAction = new QAction(this);
     act->setText(tr("Detach Debugger"));
     connect(act, SIGNAL(triggered()), SLOT(handleExecDetach()));
 
-
     Core::Command *cmd = 0;
 
     Core::ActionContainer *mstart =
@@ -1716,6 +1722,13 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
     cmd->setAttribute(Command::CA_Hide);
     mstart->addAction(cmd, CC::G_DEFAULT_ONE);
 
+    if (m_startRemoteCdbAction) {
+        cmd = am->registerAction(m_startRemoteCdbAction,
+                                 Constants::ATTACHREMOTECDB, globalcontext);
+        cmd->setAttribute(Command::CA_Hide);
+        mstart->addAction(cmd, CC::G_DEFAULT_ONE);
+    }
+
     cmd = am->registerAction(m_detachAction,
         Constants::DETACH, globalcontext);
     cmd->setAttribute(Command::CA_Hide);
@@ -2124,6 +2137,25 @@ void DebuggerPluginPrivate::attachRemote(const QString &spec)
         startDebugger(rc);
 }
 
+void DebuggerPluginPrivate::startRemoteCdbSession()
+{
+    const QString connectionKey = _("CdbRemoteConnection");
+    DebuggerStartParameters sp;
+    sp.toolChainType = ProjectExplorer::ToolChain_MSVC;
+    sp.startMode = AttachToRemote;
+    StartRemoteCdbDialog dlg(mainWindow());
+    QString previousConnection = configValue(connectionKey).toString();
+    if (previousConnection.isEmpty())
+        previousConnection = QLatin1String("localhost:1234");
+    dlg.setConnection(previousConnection);
+    if (dlg.exec() != QDialog::Accepted)
+        return;
+    sp.remoteChannel = dlg.connection();
+    setConfigValue(connectionKey, sp.remoteChannel);
+    if (RunControl *rc = createDebugger(sp))
+        startDebugger(rc);
+}
+
 void DebuggerPluginPrivate::startRemoteApplication()
 {
     DebuggerStartParameters sp;
-- 
GitLab