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 <executable></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