From a945e96377cf0c8b72a96ce6de2bb5889abc62c2 Mon Sep 17 00:00:00 2001 From: hjk <qtc-committer@nokia.com> Date: Wed, 5 May 2010 12:41:52 +0200 Subject: [PATCH] Revive TCF agent This mainly reverts commit ba47987e67cea19ce22ee186322e37fc5111a57e. Conflicts: src/plugins/debugger/debugger.pro src/plugins/debugger/debuggermanager.cpp src/plugins/debugger/debuggermanager.h src/plugins/debugger/debuggerplugin.cpp src/plugins/debugger/debuggerplugin.h --- src/plugins/debugger/Debugger.pluginspec | 1 + src/plugins/debugger/attachtcfdialog.ui | 95 ++++ src/plugins/debugger/debugger.pro | 2 + src/plugins/debugger/debuggerconstants.h | 1 + src/plugins/debugger/debuggerdialogs.cpp | 90 ++++ src/plugins/debugger/debuggerdialogs.h | 26 ++ src/plugins/debugger/debuggermanager.cpp | 20 +- src/plugins/debugger/debuggermanager.h | 4 + src/plugins/debugger/debuggerplugin.cpp | 56 ++- src/plugins/debugger/debuggerplugin.h | 2 + src/plugins/debugger/tcf/json.cpp | 372 +++++++++++++++ src/plugins/debugger/tcf/json.h | 100 ++++ src/plugins/debugger/tcf/tcf.pri | 11 + src/plugins/debugger/tcf/tcfengine.cpp | 569 +++++++++++++++++++++++ src/plugins/debugger/tcf/tcfengine.h | 168 +++++++ 15 files changed, 1511 insertions(+), 6 deletions(-) create mode 100644 src/plugins/debugger/attachtcfdialog.ui create mode 100644 src/plugins/debugger/tcf/json.cpp create mode 100644 src/plugins/debugger/tcf/json.h create mode 100644 src/plugins/debugger/tcf/tcf.pri create mode 100644 src/plugins/debugger/tcf/tcfengine.cpp create mode 100644 src/plugins/debugger/tcf/tcfengine.h diff --git a/src/plugins/debugger/Debugger.pluginspec b/src/plugins/debugger/Debugger.pluginspec index 18f56e3a383..a396d7569a0 100644 --- a/src/plugins/debugger/Debugger.pluginspec +++ b/src/plugins/debugger/Debugger.pluginspec @@ -23,6 +23,7 @@ Alternatively, this plugin may be used under the terms of the GNU Lesser General <argument name="-disable-cdb">Disable Cdb debugger engine</argument> <argument name="-disable-gdb">Disable Gdb debugger engine</argument> <argument name="-disable-sdb">Disable Qt Script debugger engine</argument> + <argument name="-disable-tcf">Disable Tcf debugger engine</argument> <argument name="-debug" parameter="pid-or-corefile">Attach to Process-Id or Core file</argument> <argument name="-wincrashevent" parameter="event-handle">Event handle used for attaching to crashed processes</argument> </argumentList> diff --git a/src/plugins/debugger/attachtcfdialog.ui b/src/plugins/debugger/attachtcfdialog.ui new file mode 100644 index 00000000000..c890f11ff95 --- /dev/null +++ b/src/plugins/debugger/attachtcfdialog.ui @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AttachTcfDialog</class> + <widget class="QDialog" name="AttachTcfDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>310</width> + <height>224</height> + </rect> + </property> + <property name="windowTitle"> + <string>Start Debugger</string> + </property> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>9</number> + </property> + <item> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="channelLabel"> + <property name="text"> + <string>Host and port:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="channelLineEdit"> + <property name="text"> + <string notr="true">localhost:5115</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="architectureLabel"> + <property name="text"> + <string>Architecture:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="architectureComboBox"/> + </item> + <item row="2" column="1"> + <widget class="QCheckBox" name="useServerStartScriptCheckBox"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="useServerStartScriptLabel"> + <property name="text"> + <string>Use server start script:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="Utils::PathChooser" name="serverStartScript" native="true"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="serverStartScriptLabel"> + <property name="text"> + <string>Server start script:</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Utils::PathChooser</class> + <extends>QWidget</extends> + <header location="global">utils/pathchooser.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index 6bbfe497439..666019ae24e 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -80,6 +80,7 @@ SOURCES += breakhandler.cpp \ FORMS += attachexternaldialog.ui \ attachcoredialog.ui \ + attachtcfdialog.ui \ breakbyfunction.ui \ breakcondition.ui \ dumperoptionpage.ui \ @@ -103,6 +104,7 @@ include(cdb/cdb.pri) include(gdb/gdb.pri) include(script/script.pri) include(pdb/pdb.pri) +include(tcf/tcf.pri) include(shared/shared.pri) OTHER_FILES += Debugger.pluginspec diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h index d958246fbf8..eb6786d9c82 100644 --- a/src/plugins/debugger/debuggerconstants.h +++ b/src/plugins/debugger/debuggerconstants.h @@ -112,6 +112,7 @@ enum DebuggerStartMode StartExternal, // Start binary found in file system AttachExternal, // Attach to running process by process id 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 }; diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp index dc36ee52ad6..3f07315ef87 100644 --- a/src/plugins/debugger/debuggerdialogs.cpp +++ b/src/plugins/debugger/debuggerdialogs.cpp @@ -32,6 +32,7 @@ #include "ui_attachcoredialog.h" #include "ui_attachexternaldialog.h" +#include "ui_attachtcfdialog.h" #include "ui_startexternaldialog.h" #include "ui_startremotedialog.h" @@ -413,6 +414,95 @@ void AttachExternalDialog::pidChanged(const QString &pid) } +/////////////////////////////////////////////////////////////////////// +// +// AttachTcfDialog +// +/////////////////////////////////////////////////////////////////////// + +AttachTcfDialog::AttachTcfDialog(QWidget *parent) + : QDialog(parent), + m_ui(new Ui::AttachTcfDialog) +{ + m_ui->setupUi(this); + m_ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + m_ui->serverStartScript->setExpectedKind(Utils::PathChooser::File); + m_ui->serverStartScript->setPromptDialogTitle(tr("Select Executable")); + + connect(m_ui->useServerStartScriptCheckBox, SIGNAL(toggled(bool)), + this, SLOT(updateState())); + + connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + updateState(); +} + +AttachTcfDialog::~AttachTcfDialog() +{ + delete m_ui; +} + +void AttachTcfDialog::setRemoteChannel(const QString &channel) +{ + m_ui->channelLineEdit->setText(channel); +} + +QString AttachTcfDialog::remoteChannel() const +{ + return m_ui->channelLineEdit->text(); +} + +void AttachTcfDialog::setRemoteArchitectures(const QStringList &list) +{ + m_ui->architectureComboBox->clear(); + if (!list.isEmpty()) { + m_ui->architectureComboBox->insertItems(0, list); + m_ui->architectureComboBox->setCurrentIndex(0); + } +} + +void AttachTcfDialog::setRemoteArchitecture(const QString &arch) +{ + int index = m_ui->architectureComboBox->findText(arch); + if (index != -1) + m_ui->architectureComboBox->setCurrentIndex(index); +} + +QString AttachTcfDialog::remoteArchitecture() const +{ + int index = m_ui->architectureComboBox->currentIndex(); + return m_ui->architectureComboBox->itemText(index); +} + +void AttachTcfDialog::setServerStartScript(const QString &scriptName) +{ + m_ui->serverStartScript->setPath(scriptName); +} + +QString AttachTcfDialog::serverStartScript() const +{ + return m_ui->serverStartScript->path(); +} + +void AttachTcfDialog::setUseServerStartScript(bool on) +{ + m_ui->useServerStartScriptCheckBox->setChecked(on); +} + +bool AttachTcfDialog::useServerStartScript() const +{ + return m_ui->useServerStartScriptCheckBox->isChecked(); +} + +void AttachTcfDialog::updateState() +{ + bool enabled = m_ui->useServerStartScriptCheckBox->isChecked(); + m_ui->serverStartScriptLabel->setEnabled(enabled); + m_ui->serverStartScript->setEnabled(enabled); +} + + /////////////////////////////////////////////////////////////////////// // // StartExternalDialog diff --git a/src/plugins/debugger/debuggerdialogs.h b/src/plugins/debugger/debuggerdialogs.h index 7b9fedca8da..13ad67358f8 100644 --- a/src/plugins/debugger/debuggerdialogs.h +++ b/src/plugins/debugger/debuggerdialogs.h @@ -42,6 +42,7 @@ class QDialogButtonBox; namespace Ui { class AttachCoreDialog; class AttachExternalDialog; +class AttachTcfDialog; class StartExternalDialog; class StartRemoteDialog; } // namespace Ui @@ -107,6 +108,31 @@ private: ProcessListFilterModel *m_model; }; +class AttachTcfDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AttachTcfDialog(QWidget *parent); + ~AttachTcfDialog(); + + void setRemoteChannel(const QString &host); + void setRemoteArchitecture(const QString &arch); + void setRemoteArchitectures(const QStringList &arches); + QString remoteChannel() const; + QString remoteArchitecture() const; + void setServerStartScript(const QString &scriptName); + QString serverStartScript() const; + void setUseServerStartScript(bool on); + bool useServerStartScript() const; + +private slots: + void updateState(); + +private: + Ui::AttachTcfDialog *m_ui; +}; + class StartExternalDialog : public QDialog { Q_OBJECT diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp index ac645e19c3f..9b09cd09b4d 100644 --- a/src/plugins/debugger/debuggermanager.cpp +++ b/src/plugins/debugger/debuggermanager.cpp @@ -157,6 +157,7 @@ namespace Internal { IDebuggerEngine *createGdbEngine(DebuggerManager *parent); IDebuggerEngine *createScriptEngine(DebuggerManager *parent); IDebuggerEngine *createPdbEngine(DebuggerManager *parent); +IDebuggerEngine *createTcfEngine(DebuggerManager *parent); // The createCdbEngine function takes a list of options pages it can add to. // This allows for having a "enabled" toggle on the page independently @@ -251,6 +252,7 @@ static Debugger::Internal::IDebuggerEngine *gdbEngine = 0; static Debugger::Internal::IDebuggerEngine *scriptEngine = 0; static Debugger::Internal::IDebuggerEngine *cdbEngine = 0; static Debugger::Internal::IDebuggerEngine *pdbEngine = 0; +static Debugger::Internal::IDebuggerEngine *tcfEngine = 0; struct DebuggerManagerPrivate { @@ -345,6 +347,7 @@ DebuggerManager::~DebuggerManager() doDelete(pdbEngine); doDelete(gdbEngine); doDelete(cdbEngine); + doDelete(tcfEngine); doDelete(d->m_breakHandler); doDelete(d->m_threadsHandler); @@ -357,6 +360,7 @@ DebuggerManager::~DebuggerManager() doDelete(gdbEngine); doDelete(scriptEngine); doDelete(cdbEngine); + doDelete(tcfEngine); #undef doDelete DebuggerManagerPrivate::instance = 0; delete d; @@ -664,6 +668,11 @@ QList<Core::IOptionsPage*> DebuggerManager::initializeEngines(unsigned enabledTy //pdbEngine->addOptionPages(&rc); } + if (enabledTypeFlags & TcfEngineType) { + tcfEngine = createTcfEngine(this); + tcfEngine->addOptionPages(&rc); + } + d->m_engine = 0; STATE_DEBUG(gdbEngine << cdbEngine << scriptEngine << pdbEngine << rc.size()); @@ -1038,6 +1047,9 @@ static IDebuggerEngine *debuggerEngineForExecutable(const QString &executable, // Debugger type for mode static IDebuggerEngine *debuggerEngineForMode(DebuggerStartMode startMode, QString *errorMessage) { + if (startMode == AttachTcf) + return tcfEngine; + #ifdef Q_OS_WIN // Preferably Windows debugger for attaching locally. if (startMode != StartRemote && cdbEngine) @@ -1676,10 +1688,12 @@ void DebuggerManager::showQtDumperLibraryWarning(const QString &details) dialog.setDetailedText(details); dialog.exec(); if (dialog.clickedButton() == qtPref) { - Core::ICore::instance()->showOptionsDialog(_(Qt4ProjectManager::Constants::QT_SETTINGS_CATEGORY), - _(Qt4ProjectManager::Constants::QTVERSION_SETTINGS_PAGE_ID)); + Core::ICore::instance()->showOptionsDialog( + _(Qt4ProjectManager::Constants::QT_SETTINGS_CATEGORY), + _(Qt4ProjectManager::Constants::QTVERSION_SETTINGS_PAGE_ID)); } else if (dialog.clickedButton() == helperOff) { - theDebuggerAction(UseDebuggingHelpers)->setValue(qVariantFromValue(false), false); + theDebuggerAction(UseDebuggingHelpers) + ->setValue(qVariantFromValue(false), false); } } diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h index 9c2ee51f876..6ba60846420 100644 --- a/src/plugins/debugger/debuggermanager.h +++ b/src/plugins/debugger/debuggermanager.h @@ -92,6 +92,7 @@ class CdbDumperHelper; class CdbDumperInitThread; class CdbExceptionLoggerEventCallback; class GdbEngine; +class TcfEngine; class CdbDebugEngine; class CdbDebugEnginePrivate; class TrkGdbAdapter; @@ -140,10 +141,12 @@ enum DebuggerEngineTypeFlags ScriptEngineType = 0x02, CdbEngineType = 0x04, PdbEngineType = 0x08, + TcfEngineType = 0x10, AllEngineTypes = GdbEngineType | ScriptEngineType | CdbEngineType | PdbEngineType + | TcfEngineType }; QDebug operator<<(QDebug d, DebuggerState state); @@ -171,6 +174,7 @@ public: friend class Internal::GdbEngine; friend class Internal::ScriptEngine; friend class Internal::PdbEngine; + friend class Internal::TcfEngine; friend class Internal::CdbDebugEngine; friend class Internal::CdbDebugEnginePrivate; friend class Internal::TrkGdbAdapter; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 530eca53e4e..70541e03d69 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -122,6 +122,7 @@ const char * const M_DEBUG_START_DEBUGGING = "QtCreator.Menu.Debug.StartDebuggin const char * const STARTEXTERNAL = "Debugger.StartExternal"; 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 DETACH = "Debugger.Detach"; @@ -625,7 +626,7 @@ static bool parseArgument(QStringList::const_iterator &it, } return true; } - // engine disabling + // Engine disabling. if (option == _("-disable-cdb")) { *enabledEngines &= ~Debugger::CdbEngineType; return true; @@ -638,14 +639,18 @@ static bool parseArgument(QStringList::const_iterator &it, *enabledEngines &= ~Debugger::ScriptEngineType; return true; } + if (option == QLatin1String("-disable-tcf")) { + *enabledEngines &= ~TcfEngineType; + return true; + } *errorMessage = DebuggerPlugin::tr("Invalid debugger option: %1").arg(option); return false; } static bool parseArguments(const QStringList &args, - DebuggerPlugin::AttachRemoteParameters *attachRemoteParameters, - unsigned *enabledEngines, QString *errorMessage) + DebuggerPlugin::AttachRemoteParameters *attachRemoteParameters, + unsigned *enabledEngines, QString *errorMessage) { const QStringList::const_iterator cend = args.constEnd(); for (QStringList::const_iterator it = args.constBegin(); it != cend; ++it) @@ -747,6 +752,13 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess m_attachCoreAction->setText(tr("Attach to Core...")); connect(m_attachCoreAction, SIGNAL(triggered()), this, SLOT(attachCore())); + m_attachTcfAction = new QAction(this); + m_attachTcfAction->setText(tr("Attach to Running Tcf Agent...")); + m_attachTcfAction->setToolTip(tr("This attaches to a running " + "'Target Communication Framework' agent.")); + connect(m_attachTcfAction, SIGNAL(triggered()), + this, SLOT(attachRemoteTcf())); + m_startRemoteAction = new QAction(this); m_startRemoteAction->setText(tr("Start and Attach to Remote Application...")); connect(m_startRemoteAction, SIGNAL(triggered()), @@ -779,9 +791,14 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess cmd = am->registerAction(m_attachCoreAction, Constants::ATTACHCORE, globalcontext); + cmd->setAttribute(Command::CA_Hide); mstart->addAction(cmd, CC::G_DEFAULT_ONE); + cmd = am->registerAction(m_attachTcfAction, + Constants::ATTACHTCF, globalcontext); + mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE); + cmd = am->registerAction(m_startRemoteAction, Constants::ATTACHREMOTE, globalcontext); cmd->setAttribute(Command::CA_Hide); @@ -1520,6 +1537,39 @@ void DebuggerPlugin::toggleBreakpoint() m_manager->toggleBreakpoint(fileName, lineNumber); } +void DebuggerPlugin::attachRemoteTcf() +{ + const DebuggerStartParametersPtr sp(new DebuggerStartParameters); + AttachTcfDialog dlg(m_uiSwitcher->mainWindow()); + QStringList arches; + arches.append(_("i386:x86-64:intel")); + dlg.setRemoteArchitectures(arches); + dlg.setRemoteChannel( + configValue(_("LastTcfRemoteChannel")).toString()); + dlg.setRemoteArchitecture( + configValue(_("LastTcfRemoteArchitecture")).toString()); + dlg.setServerStartScript( + configValue(_("LastTcfServerStartScript")).toString()); + dlg.setUseServerStartScript( + configValue(_("LastTcfUseServerStartScript")).toBool()); + if (dlg.exec() != QDialog::Accepted) + return; + setConfigValue(_("LastTcfRemoteChannel"), dlg.remoteChannel()); + setConfigValue(_("LastTcfRemoteArchitecture"), dlg.remoteArchitecture()); + setConfigValue(_("LastTcfServerStartScript"), dlg.serverStartScript()); + setConfigValue(_("LastTcfUseServerStartScript"), dlg.useServerStartScript()); + sp->remoteChannel = dlg.remoteChannel(); + sp->remoteArchitecture = dlg.remoteArchitecture(); + sp->serverStartScript = dlg.serverStartScript(); + sp->startMode = AttachTcf; + if (dlg.useServerStartScript()) + sp->serverStartScript = dlg.serverStartScript(); + + if (RunControl *runControl = m_debuggerRunControlFactory->create(sp)) + ProjectExplorerPlugin::instance() + ->startRunControl(runControl, PE::DEBUGMODE); +} + #include "debuggerplugin.moc" Q_EXPORT_PLUGIN(DebuggerPlugin) diff --git a/src/plugins/debugger/debuggerplugin.h b/src/plugins/debugger/debuggerplugin.h index bb53708f63c..251446db0a8 100644 --- a/src/plugins/debugger/debuggerplugin.h +++ b/src/plugins/debugger/debuggerplugin.h @@ -120,6 +120,7 @@ private slots: void attachExternalApplication(); void attachCore(); void attachCmdLine(); + void attachRemoteTcf(); void enableReverseDebuggingTriggered(const QVariant &value); void languageChanged(const QString &debuggerLanguage); @@ -153,6 +154,7 @@ private: QAction *m_startRemoteAction; QAction *m_attachExternalAction; QAction *m_attachCoreAction; + QAction *m_attachTcfAction; QAction *m_detachAction; QComboBox *m_langBox; QToolButton *m_reverseToolButton; diff --git a/src/plugins/debugger/tcf/json.cpp b/src/plugins/debugger/tcf/json.cpp new file mode 100644 index 00000000000..f8bf75b0ca5 --- /dev/null +++ b/src/plugins/debugger/tcf/json.cpp @@ -0,0 +1,372 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 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 "json.h" + +#include <utils/qtcassert.h> + +#include <QtCore/QByteArray> +#include <QtCore/QTextStream> + +#include <ctype.h> + +//#define DEBUG_JASON +#ifdef DEBUG_JASON +#define JDEBUG(s) qDebug() << s +#else +#define JDEBUG(s) +#endif + +namespace Debugger { +namespace Internal { + +static void skipSpaces(const char *&from, const char *to) +{ + while (from != to && isspace(*from)) + ++from; +} + +QTextStream &operator<<(QTextStream &os, const JsonValue &mi) +{ + return os << mi.toString(); +} + +void JsonValue::parsePair(const char *&from, const char *to) +{ + skipSpaces(from, to); + JDEBUG("parsePair: " << QByteArray(from, to - from)); + m_name = parseCString(from, to); + skipSpaces(from, to); + while (from < to && *from != ':') { + JDEBUG("not a colon" << *from); + ++from; + } + ++from; + parseValue(from, to); + skipSpaces(from, to); +} + +QByteArray JsonValue::parseNumber(const char *&from, const char *to) +{ + QByteArray result; + while (from < to && *from >= '0' && *from <= '9') + result.append(*from++); + return result; +} + +QByteArray JsonValue::parseCString(const char *&from, const char *to) +{ + QByteArray result; + JDEBUG("parseCString: " << QByteArray(from, to - from)); + if (*from != '"') { + qDebug() << "JSON Parse Error, double quote expected"; + ++from; // So we don't hang + return QByteArray(); + } + const char *ptr = from; + ++ptr; + while (ptr < to) { + if (*ptr == '"') { + ++ptr; + result = QByteArray(from + 1, ptr - from - 2); + break; + } + if (*ptr == '\\') { + ++ptr; + if (ptr == to) { + qDebug() << "JSON Parse Error, unterminated backslash escape"; + from = ptr; // So we don't hang + return QByteArray(); + } + } + ++ptr; + } + from = ptr; + + int idx = result.indexOf('\\'); + if (idx >= 0) { + char *dst = result.data() + idx; + const char *src = dst + 1, *end = result.data() + result.length(); + do { + char c = *src++; + switch (c) { + case 'a': *dst++ = '\a'; break; + case 'b': *dst++ = '\b'; break; + case 'f': *dst++ = '\f'; break; + case 'n': *dst++ = '\n'; break; + case 'r': *dst++ = '\r'; break; + case 't': *dst++ = '\t'; break; + case 'v': *dst++ = '\v'; break; + case '"': *dst++ = '"'; break; + case '\\': *dst++ = '\\'; break; + default: + { + int chars = 0; + uchar prod = 0; + forever { + if (c < '0' || c > '7') { + --src; + break; + } + prod = prod * 8 + c - '0'; + if (++chars == 3 || src == end) + break; + c = *src++; + } + if (!chars) { + qDebug() << "JSON Parse Error, unrecognized backslash escape"; + return QByteArray(); + } + *dst++ = prod; + } + } + while (src != end) { + char c = *src++; + if (c == '\\') + break; + *dst++ = c; + } + } while (src != end); + *dst = 0; + result.truncate(dst - result.data()); + } + + JDEBUG("parseCString, got " << result); + return result; +} + +void JsonValue::parseValue(const char *&from, const char *to) +{ + JDEBUG("parseValue: " << QByteArray(from, to - from)); + switch (*from) { + case '{': + parseObject(from, to); + break; + case '[': + parseArray(from, to); + break; + case '"': + m_type = String; + m_data = parseCString(from, to); + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + m_type = Number; + m_data = parseNumber(from, to); + default: + break; + } +} + +void JsonValue::parseObject(const char *&from, const char *to) +{ + JDEBUG("parseObject: " << QByteArray(from, to - from)); + QTC_ASSERT(*from == '{', /**/); + ++from; + m_type = Object; + while (from < to) { + if (*from == '}') { + ++from; + break; + } + JsonValue child; + child.parsePair(from, to); + if (!child.isValid()) + return; + m_children += child; + if (*from == ',') + ++from; + } +} + +void JsonValue::parseArray(const char *&from, const char *to) +{ + JDEBUG("parseArray: " << QByteArray(from, to - from)); + QTC_ASSERT(*from == '[', /**/); + ++from; + m_type = Array; + while (from < to) { + if (*from == ']') { + ++from; + break; + } + JsonValue child; + child.parseValue(from, to); + if (child.isValid()) + m_children += child; + if (*from == ',') + ++from; + } +} + +void JsonValue::setStreamOutput(const QByteArray &name, const QByteArray &content) +{ + if (content.isEmpty()) + return; + JsonValue child; + child.m_type = String; + child.m_name = name; + child.m_data = content; + m_children += child; + if (m_type == Invalid) + m_type = Object; +} + +static QByteArray ind(int indent) +{ + return QByteArray(2 * indent, ' '); +} + +void JsonValue::dumpChildren(QByteArray * str, bool multiline, int indent) const +{ + for (int i = 0; i < m_children.size(); ++i) { + if (i != 0) { + *str += ','; + if (multiline) + *str += '\n'; + } + if (multiline) + *str += ind(indent); + *str += m_children.at(i).toString(multiline, indent); + } +} + +class MyString : public QString { +public: + ushort at(int i) const { return constData()[i].unicode(); } +}; + +template<class ST, typename CT> +inline ST escapeCStringTpl(const ST &ba) +{ + ST ret; + ret.reserve(ba.length() * 2); + for (int i = 0; i < ba.length(); ++i) { + CT c = ba.at(i); + switch (c) { + case '\\': ret += "\\\\"; break; + case '\a': ret += "\\a"; break; + case '\b': ret += "\\b"; break; + case '\f': ret += "\\f"; break; + case '\n': ret += "\\n"; break; + case '\r': ret += "\\r"; break; + case '\t': ret += "\\t"; break; + case '\v': ret += "\\v"; break; + case '"': ret += "\\\""; break; + default: + if (c < 32 || c == 127) { + ret += '\\'; + ret += '0' + (c >> 6); + ret += '0' + ((c >> 3) & 7); + ret += '0' + (c & 7); + } else { + ret += c; + } + } + } + return ret; +} + +QString JsonValue::escapeCString(const QString &ba) +{ + return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba)); +} + +QByteArray JsonValue::escapeCString(const QByteArray &ba) +{ + return escapeCStringTpl<QByteArray, uchar>(ba); +} + +QByteArray JsonValue::toString(bool multiline, int indent) const +{ + QByteArray result; + switch (m_type) { + case Invalid: + if (multiline) + result += ind(indent) + "Invalid\n"; + else + result += "Invalid"; + break; + case String: + if (!m_name.isEmpty()) + result += m_name + "="; + result += '"' + escapeCString(m_data) + '"'; + break; + case Number: + if (!m_name.isEmpty()) + result += '"' + m_name + "\":"; + result += m_data; + break; + case Object: + if (!m_name.isEmpty()) + result += m_name + '='; + if (multiline) { + result += "{\n"; + dumpChildren(&result, multiline, indent + 1); + result += '\n' + ind(indent) + "}"; + } else { + result += "{"; + dumpChildren(&result, multiline, indent + 1); + result += "}"; + } + break; + case Array: + if (!m_name.isEmpty()) + result += m_name + "="; + if (multiline) { + result += "[\n"; + dumpChildren(&result, multiline, indent + 1); + result += '\n' + ind(indent) + "]"; + } else { + result += "["; + dumpChildren(&result, multiline, indent + 1); + result += "]"; + } + break; + } + return result; +} + +void JsonValue::fromString(const QByteArray &ba) +{ + const char *from = ba.constBegin(); + const char *to = ba.constEnd(); + parseValue(from, to); +} + +JsonValue JsonValue::findChild(const char *name) const +{ + for (int i = 0; i < m_children.size(); ++i) + if (m_children.at(i).m_name == name) + return m_children.at(i); + return JsonValue(); +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/tcf/json.h b/src/plugins/debugger/tcf/json.h new file mode 100644 index 00000000000..18c42a57b6f --- /dev/null +++ b/src/plugins/debugger/tcf/json.h @@ -0,0 +1,100 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 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 DEBUGGER_JSON_H +#define DEBUGGER_JSON_H + +#include <QtCore/QByteArray> +#include <QtCore/QList> + +namespace Debugger { +namespace Internal { + +class JsonValue +{ +public: + JsonValue() : m_type(Invalid) {} + explicit JsonValue(const QByteArray &str) { fromString(str); } + + QByteArray m_name; + QByteArray m_data; + QList<JsonValue> m_children; + + enum Type { + Invalid, + String, + Number, + Object, + Array, + }; + + Type m_type; + + inline Type type() const { return m_type; } + inline QByteArray name() const { return m_name; } + inline bool hasName(const char *name) const { return m_name == name; } + + inline bool isValid() const { return m_type != Invalid; } + inline bool isNumber() const { return m_type == Number; } + inline bool isString() const { return m_type == String; } + inline bool isObject() const { return m_type == Object; } + inline bool isArray() const { return m_type == Array; } + + + inline QByteArray data() const { return m_data; } + inline const QList<JsonValue> &children() const { return m_children; } + inline int childCount() const { return m_children.size(); } + + const JsonValue &childAt(int index) const { return m_children[index]; } + JsonValue &childAt(int index) { return m_children[index]; } + JsonValue findChild(const char *name) const; + + QByteArray toString(bool multiline = false, int indent = 0) const; + void fromString(const QByteArray &str); + void setStreamOutput(const QByteArray &name, const QByteArray &content); + +private: + static QByteArray parseCString(const char *&from, const char *to); + static QByteArray parseNumber(const char *&from, const char *to); + static QByteArray escapeCString(const QByteArray &ba); + static QString escapeCString(const QString &ba); + void parsePair(const char *&from, const char *to); + void parseValue(const char *&from, const char *to); + void parseObject(const char *&from, const char *to); + void parseArray(const char *&from, const char *to); + + void dumpChildren(QByteArray *str, bool multiline, int indent) const; +}; + +} // namespace Internal +} // namespace Debugger + +//Q_DECLARE_METATYPE(GdbDebugger::Internal::JsonValue); + +#endif // DEBUGGER_JSON_H diff --git a/src/plugins/debugger/tcf/tcf.pri b/src/plugins/debugger/tcf/tcf.pri new file mode 100644 index 00000000000..faaad28f759 --- /dev/null +++ b/src/plugins/debugger/tcf/tcf.pri @@ -0,0 +1,11 @@ +HEADERS += \ + $$PWD/json.h \ + $$PWD/tcfengine.h \ + +SOURCES += \ + $$PWD/json.cpp \ + $$PWD/tcfengine.cpp \ + +FORMS += + +RESOURCES += diff --git a/src/plugins/debugger/tcf/tcfengine.cpp b/src/plugins/debugger/tcf/tcfengine.cpp new file mode 100644 index 00000000000..7982e4e5192 --- /dev/null +++ b/src/plugins/debugger/tcf/tcfengine.cpp @@ -0,0 +1,569 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 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 "tcfengine.h" + +#include "debuggerstringutils.h" +#include "debuggerdialogs.h" +#include "breakhandler.h" +#include "debuggerconstants.h" +#include "debuggermanager.h" +#include "moduleshandler.h" +#include "registerhandler.h" +#include "stackhandler.h" +#include "watchhandler.h" +#include "watchutils.h" +#include "moduleshandler.h" +#include "json.h" + +#include <utils/qtcassert.h> + +#include <QtCore/QDateTime> +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QTimer> + +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QMainWindow> +#include <QtGui/QMessageBox> +#include <QtGui/QToolTip> + +#include <QtNetwork/QTcpSocket> + +#define DEBUG_TCF 1 +#if DEBUG_TCF +# define SDEBUG(s) qDebug() << s +#else +# define SDEBUG(s) +#endif +# define XSDEBUG(s) qDebug() << s + +#define CB(callback) &TcfEngine::callback, STRINGIFY(callback) + +//#define USE_CONGESTION_CONTROL + +static QByteArray C(const QByteArray &ba1, + const QByteArray &ba2 = QByteArray(), + const QByteArray &ba3 = QByteArray(), + const QByteArray &ba4 = QByteArray(), + const QByteArray &ba5 = QByteArray()) +{ + QByteArray result = ba1; + if (!ba2.isEmpty()) { result += '\0'; result += ba2; } + if (!ba3.isEmpty()) { result += '\0'; result += ba3; } + if (!ba4.isEmpty()) { result += '\0'; result += ba4; } + if (!ba5.isEmpty()) { result += '\0'; result += ba5; } + return result; +} + +namespace Debugger { +namespace Internal { + +/////////////////////////////////////////////////////////////////////// +// +// TcfCommand +// +/////////////////////////////////////////////////////////////////////// + + +QString TcfEngine::TcfCommand::toString() const +{ + return quoteUnprintableLatin1(command); +} + + +/////////////////////////////////////////////////////////////////////// +// +// TcfEngine +// +/////////////////////////////////////////////////////////////////////// + +TcfEngine::TcfEngine(DebuggerManager *manager) + : IDebuggerEngine(manager) +{ + m_congestion = 0; + m_inAir = 0; + + m_sendTimer.setSingleShot(true); + m_sendTimer.setInterval(100); // ms + connect(&m_sendTimer, SIGNAL(timeout()), this, SLOT(handleSendTimer())); + + m_socket = new QTcpSocket(this); + connect(m_socket, SIGNAL(connected()), this, SLOT(socketConnected())); + connect(m_socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); + connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(socketError(QAbstractSocket::SocketError))); + + //void aboutToClose () + //void bytesWritten ( qint64 bytes ) + //void readChannelFinished () + connect(m_socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead())); + + //connect(m_socket, SIGNAL(hostFound()) + //connect(m_socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy, QAuthenticator *))) + //connect(m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + // thism SLOT(socketStateChanged(QAbstractSocket::SocketState))); +} + +TcfEngine::~TcfEngine() +{ +} + +void TcfEngine::socketReadyRead() +{ + //XSDEBUG("TcfEngine::socketReadyRead()"); + m_inbuffer.append(m_socket->readAll()); + int pos = 0; + while (1) { + // the "\3" is followed by either "\1" or "\2" + int next = m_inbuffer.indexOf("\3", pos); + //qDebug() << "pos: " << pos << "next: " << next; + if (next == -1) + break; + handleResponse(m_inbuffer.mid(pos, next - pos)); + pos = next + 2; + } + m_inbuffer.clear(); +} + +void TcfEngine::socketConnected() +{ + showStatusMessage("Socket connected."); + m_socket->waitForConnected(2000); + //sendCommand("Locator", "redirect", "ID"); +} + +void TcfEngine::socketDisconnected() +{ + XSDEBUG("FIXME: TcfEngine::socketDisconnected()"); +} + +void TcfEngine::socketError(QAbstractSocket::SocketError) +{ + QString msg = tr("%1.").arg(m_socket->errorString()); + //QMessageBox::critical(q->mainWindow(), tr("Error"), msg); + showStatusMessage(msg); + manager()->notifyInferiorExited(); +} + +void TcfEngine::executeDebuggerCommand(const QString &command) +{ + QByteArray cmd = command.toUtf8(); + cmd = cmd.mid(cmd.indexOf(' ') + 1); + QByteArray null; + null.append('\0'); + // FIXME: works for single-digit escapes only + cmd.replace("\\0", null); + cmd.replace("\\1", "\1"); + cmd.replace("\\3", "\3"); + TcfCommand tcf; + tcf.command = cmd; + enqueueCommand(tcf); +} + +void TcfEngine::shutdown() +{ + m_congestion = 0; + m_inAir = 0; + m_services.clear(); + exitDebugger(); +} + +void TcfEngine::exitDebugger() +{ + SDEBUG("TcfEngine::exitDebugger()"); + manager()->notifyInferiorExited(); +} + +void TcfEngine::startDebugger(const DebuggerStartParametersPtr &sp) +{ + setState(InferiorRunningRequested); + showStatusMessage(tr("Running requested..."), 5000); + const int pos = sp->remoteChannel.indexOf(QLatin1Char(':')); + const QString host = sp->remoteChannel.left(pos); + const quint16 port = sp->remoteChannel.mid(pos + 1).toInt(); + //QTimer::singleShot(0, this, SLOT(runInferior())); + m_socket->connectToHost(host, port); + emit startSuccessful(); +} + +void TcfEngine::continueInferior() +{ + SDEBUG("TcfEngine::continueInferior()"); +} + +void TcfEngine::runInferior() +{ +} + +void TcfEngine::interruptInferior() +{ + XSDEBUG("TcfEngine::interruptInferior()"); +} + +void TcfEngine::executeStep() +{ + //SDEBUG("TcfEngine::executeStep()"); +} + +void TcfEngine::executeStepI() +{ + //SDEBUG("TcfEngine::executeStepI()"); +} + +void TcfEngine::executeStepOut() +{ + //SDEBUG("TcfEngine::executeStepOut()"); +} + +void TcfEngine::executeNext() +{ + //SDEBUG("TcfEngine::nextExec()"); +} + +void TcfEngine::executeNextI() +{ + //SDEBUG("TcfEngine::executeNextI()"); +} + +void TcfEngine::executeRunToLine(const QString &fileName, int lineNumber) +{ + Q_UNUSED(fileName) + Q_UNUSED(lineNumber) + SDEBUG("FIXME: TcfEngine::executeRunToLine()"); +} + +void TcfEngine::executeRunToFunction(const QString &functionName) +{ + Q_UNUSED(functionName) + XSDEBUG("FIXME: TcfEngine::executeRunToFunction()"); +} + +void TcfEngine::executeJumpToLine(const QString &fileName, int lineNumber) +{ + Q_UNUSED(fileName) + Q_UNUSED(lineNumber) + XSDEBUG("FIXME: TcfEngine::executeJumpToLine()"); +} + +void TcfEngine::activateFrame(int index) +{ + Q_UNUSED(index) +} + +void TcfEngine::selectThread(int index) +{ + Q_UNUSED(index) +} + +void TcfEngine::attemptBreakpointSynchronization() +{ +} + +void TcfEngine::loadSymbols(const QString &moduleName) +{ + Q_UNUSED(moduleName) +} + +void TcfEngine::loadAllSymbols() +{ +} + +void TcfEngine::reloadModules() +{ +} + +void TcfEngine::requestModuleSymbols(const QString &moduleName) +{ + Q_UNUSED(moduleName) +} + + +void TcfEngine::handleResponse(const QByteArray &response) +{ + static QTime lastTime; + + //debugMessage(_(" "), currentTime()); + QList<QByteArray> parts = response.split('\0'); + if (parts.size() < 2 || !parts.last().isEmpty()) { + SDEBUG("WRONG RESPONSE PACKET LAYOUT" << parts); + //if (response.isEmpty()) + acknowledgeResult(); + return; + } + parts.removeLast(); // always empty + QByteArray tag = parts.at(0); + int n = parts.size(); + if (n == 2 && tag == "N") { // unidentified command + int token = parts.at(1).toInt(); + TcfCommand tcf = m_cookieForToken[token]; + SDEBUG("COMMAND NOT RECOGNIZED FOR TOKEN" << token << tcf.toString()); + showDebuggerOutput(LogOutput, QString::number(token) + "^" + + "NOT RECOQNIZED: " + quoteUnprintableLatin1(response)); + acknowledgeResult(); + } else if (n == 2 && tag == "F") { // flow control + m_congestion = parts.at(1).toInt(); + SDEBUG("CONGESTION: " << m_congestion); + } else if (n == 4 && tag == "R") { // result data + acknowledgeResult(); + int token = parts.at(1).toInt(); + QByteArray message = parts.at(2); + JsonValue data(parts.at(3)); + showDebuggerOutput(LogOutput, QString("%1^%2%3").arg(token) + .arg(quoteUnprintableLatin1(response)) + .arg(QString::fromUtf8(data.toString()))); + TcfCommand tcf = m_cookieForToken[token]; + JsonValue result(data); + SDEBUG("GOOD RESPONSE: " << quoteUnprintableLatin1(response)); + if (tcf.callback) + (this->*(tcf.callback))(result, tcf.cookie); + } else if (n == 3 && tag == "P") { // progress data (partial result) + //int token = parts.at(1).toInt(); + QByteArray data = parts.at(2); + SDEBUG(_("\nTCF PARTIAL:") << quoteUnprintableLatin1(response)); + } else if (n == 4 && tag == "E") { // an event + QByteArray service = parts.at(1); + QByteArray eventName = parts.at(2); + JsonValue data(parts.at(3)); + if (eventName != "peerHeartBeat") + SDEBUG(_("\nTCF EVENT:") << quoteUnprintableLatin1(response) + << data.toString()); + if (service == "Locator" && eventName == "Hello") { + m_services.clear(); + foreach (const JsonValue &service, data.children()) + m_services.append(service.data()); + QTimer::singleShot(0, this, SLOT(startDebugging())); + } + } else { + SDEBUG("UNKNOWN RESPONSE PACKET:" + << quoteUnprintableLatin1(response) << parts); + } +} + +void TcfEngine::startDebugging() +{ + //foreach (const QByteArray &service, m_services) { + // postCommand(CB(handleRunControlGetChildren), + // service, "getChildren", "\"\""); + //} + + postCommand(C("Diagnostics", "getChildren", "\"\""), + CB(handleRunControlGetChildren)); + postCommand(C("Streams", "getChildren", "\"\"")); + postCommand(C("Expressions", "getChildren", "\"\"")); + postCommand(C("SysMonitor", "getChildren", "\"\"")); + //postCommand(C("FileSystem", "getChildren", "\"\"")); + //postCommand(C("Processes", "getChildren", "\"\"")); + //postCommand(CB(handleRunControlGetChildren), "LineNumbers", "getChildren"); + //postCommand(CB(handleRunControlGetChildren), "Symbols", "getChildren"); + //postCommand(CB(handleRunControlGetChildren), "StackTrace", "getChildren"); + //postCommand(CB(handleRunControlGetChildren), "Registers", "getChildren"); + //postCommand(CB(handleRunControlGetChildren), "Memory", "getChildren"); + //postCommand(CB(handleRunControlGetChildren), "Breakpoints", "getChildren"); + //postCommand(CB(handleRunControlGetChildren), "RunControl", "getChildren"); + //postCommand(CB(handleRunControlGetChildren), "Locator", "getChildren"); + + + //postCommand(CB(handleRunControlSuspend), + // "RunControl", "suspend", "\"Thread1\""); + //postCommand(CB(handleRunControlSuspend), + // "RunControl", "getContext", "\"P12318\""); + + //postCommand(C("Locator", "sync"), CB(handleRunControlGetChildren)); + //postCommand("Locator", "redirect", "ID"); + + //postCommand(C("FileSystem", "open", "\"/bin/ls\"", "1", "2", "3"), + // CB(handleRunControlGetChildren)); + postCommand(C("FileSystem", "stat", "\"/bin/ls\""), + CB(handleRunControlGetChildren)); +} + +void TcfEngine::postCommand(const QByteArray &cmd, + TcfCommandCallback callback, const char *callbackName) +{ + static int token = 20; + ++token; + + //const char marker_eom = -1; + //const char marker_eos = -2; + //const char marker_null = -3; + + QByteArray ba = "C"; + ba.append('\0'); + ba.append(QByteArray::number(token)); + ba.append('\0'); + ba.append(cmd); + ba.append('\0'); + ba.append('\3'); + ba.append('\1'); + + TcfCommand tcf; + tcf.command = ba; + tcf.callback = callback; + tcf.callbackName = callbackName; + tcf.token = token; + + m_cookieForToken[token] = tcf; + + enqueueCommand(tcf); +} + +// Congestion control does not seem to work that way. Basically it's +// already too late when we get a flow control packet +void TcfEngine::enqueueCommand(const TcfCommand &cmd) +{ +#ifdef USE_CONGESTION_CONTROL + // congestion controled + if (m_congestion <= 0 && m_sendQueue.isEmpty()) { + //SDEBUG("DIRECT SEND" << cmd.toString()); + sendCommandNow(cmd); + } else { + SDEBUG("QUEUE " << cmd.toString()); + m_sendQueue.enqueue(cmd); + m_sendTimer.start(); + } +#else + // synchrounously + if (m_inAir == 0) + sendCommandNow(cmd); + else + m_sendQueue.enqueue(cmd); +#endif +} + +void TcfEngine::handleSendTimer() +{ + QTC_ASSERT(!m_sendQueue.isEmpty(), return); + + if (m_congestion > 0) { + // not ready... + SDEBUG("WAITING FOR CONGESTION TO GO DOWN..."); + m_sendTimer.start(); + } else { + // go! + sendCommandNow(m_sendQueue.dequeue()); + } +} + +void TcfEngine::sendCommandNow(const TcfCommand &cmd) +{ + ++m_inAir; + int result = m_socket->write(cmd.command); + Q_UNUSED(result) + m_socket->flush(); + showDebuggerInput(LogInput, QString::number(cmd.token) + " " + cmd.toString()); + SDEBUG("SEND " << cmd.toString()); //<< " " << QString::number(result)); +} + +void TcfEngine::acknowledgeResult() +{ +#if !defined(USE_CONGESTION_CONTROL) + QTC_ASSERT(m_inAir == 1, /**/); + m_inAir = 0; + if (!m_sendQueue.isEmpty()) + sendCommandNow(m_sendQueue.dequeue()); +#endif +} + +void TcfEngine::handleRunControlSuspend(const JsonValue &data, const QVariant &) +{ + SDEBUG("HANDLE RESULT" << data.toString()); +} + +void TcfEngine::handleRunControlGetChildren(const JsonValue &data, const QVariant &) +{ + SDEBUG("HANDLE RUN CONTROL GET CHILDREN" << data.toString()); +} + +void TcfEngine::handleSysMonitorGetChildren(const JsonValue &data, const QVariant &) +{ + SDEBUG("HANDLE RUN CONTROL GET CHILDREN" << data.toString()); +} + + +////////////////////////////////////////////////////////////////////// +// +// Tooltip specific stuff +// +////////////////////////////////////////////////////////////////////// + +static WatchData m_toolTip; +static QPoint m_toolTipPos; +static QHash<QString, WatchData> m_toolTipCache; + +void TcfEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos) +{ + Q_UNUSED(mousePos) + Q_UNUSED(editor) + Q_UNUSED(cursorPos) +} + +////////////////////////////////////////////////////////////////////// +// +// Watch specific stuff +// +////////////////////////////////////////////////////////////////////// + +void TcfEngine::assignValueInDebugger(const QString &expression, + const QString &value) +{ + XSDEBUG("ASSIGNING: " << expression + '=' + value); + updateLocals(); +} + +void TcfEngine::updateLocals() +{ +} + +void TcfEngine::updateWatchData(const WatchData &) +{ + //qq->watchHandler()->rebuildModel(); + showStatusMessage(tr("Stopped."), 5000); +} + +void TcfEngine::updateSubItem(const WatchData &data0) +{ + Q_UNUSED(data0) + QTC_ASSERT(false, return); +} + +void TcfEngine::debugMessage(const QString &msg) +{ + showDebuggerOutput(LogDebug, msg); +} + +IDebuggerEngine *createTcfEngine(DebuggerManager *manager) +{ + return new TcfEngine(manager); +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/tcf/tcfengine.h b/src/plugins/debugger/tcf/tcfengine.h new file mode 100644 index 00000000000..79b8079d7d1 --- /dev/null +++ b/src/plugins/debugger/tcf/tcfengine.h @@ -0,0 +1,168 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 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 DEBUGGER_TCFENGINE_H +#define DEBUGGER_TCFENGINE_H + +#include <QtCore/QByteArray> +#include <QtCore/QHash> +#include <QtCore/QMap> +#include <QtCore/QObject> +#include <QtCore/QPoint> +#include <QtCore/QProcess> +#include <QtCore/QQueue> +#include <QtCore/QSet> +#include <QtCore/QTimer> +#include <QtCore/QVariant> + +#include <QtNetwork/QAbstractSocket> + +QT_BEGIN_NAMESPACE +class QTcpSocket; +QT_END_NAMESPACE + +#include "idebuggerengine.h" +#include "debuggermanager.h" +#include "json.h" + +namespace Debugger { +namespace Internal { + +class ScriptAgent; +class WatchData; + +class TcfEngine : public IDebuggerEngine +{ + Q_OBJECT + +public: + explicit TcfEngine(DebuggerManager *parent); + ~TcfEngine(); + +private: + // IDebuggerEngine implementation + void executeStep(); + void executeStepOut(); + void executeNext(); + void executeStepI(); + void executeNextI(); + + void shutdown(); + void setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos); + void startDebugger(const DebuggerStartParametersPtr &sp); + void exitDebugger(); + + void continueInferior(); + Q_SLOT void runInferior(); + void interruptInferior(); + + void executeRunToLine(const QString &fileName, int lineNumber); + void executeRunToFunction(const QString &functionName); + void executeJumpToLine(const QString &fileName, int lineNumber); + + void activateFrame(int index); + void selectThread(int index); + + void attemptBreakpointSynchronization(); + + void assignValueInDebugger(const QString &expr, const QString &value); + void executeDebuggerCommand(const QString & command); + + void loadSymbols(const QString &moduleName); + void loadAllSymbols(); + void requestModuleSymbols(const QString &moduleName); + void reloadModules(); + void reloadRegisters() {} + void reloadSourceFiles() {} + void reloadFullStack() {} + + bool supportsThreads() const { return true; } + void maybeBreakNow(bool byFunction); + void updateWatchData(const WatchData &data); + void updateLocals(); + void updateSubItem(const WatchData &data); + + Q_SLOT void socketConnected(); + Q_SLOT void socketDisconnected(); + Q_SLOT void socketError(QAbstractSocket::SocketError); + Q_SLOT void socketReadyRead(); + + void handleResponse(const QByteArray &ba); + void handleRunControlSuspend(const JsonValue &response, const QVariant &); + void handleRunControlGetChildren(const JsonValue &response, const QVariant &); + void handleSysMonitorGetChildren(const JsonValue &response, const QVariant &); + +private: + Q_SLOT void startDebugging(); + + typedef void (TcfEngine::*TcfCommandCallback) + (const JsonValue &record, const QVariant &cookie); + + struct TcfCommand + { + TcfCommand() : flags(0), token(-1), callback(0), callbackName(0) {} + + QString toString() const; + + int flags; + int token; + TcfCommandCallback callback; + const char *callbackName; + QByteArray command; + QVariant cookie; + }; + + void postCommand(const QByteArray &cmd, + TcfCommandCallback callback = 0, const char *callbackName = 0); + void sendCommandNow(const TcfCommand &command); + void debugMessage(const QString &msg); + + QHash<int, TcfCommand> m_cookieForToken; + + QQueue<TcfCommand> m_sendQueue; + + // timer based congestion control. does not seem to work well. + void enqueueCommand(const TcfCommand &command); + Q_SLOT void handleSendTimer(); + int m_congestion; + QTimer m_sendTimer; + + // synchrounous communication + void acknowledgeResult(); + int m_inAir; + + QTcpSocket *m_socket; + QByteArray m_inbuffer; + QList<QByteArray> m_services; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_TCFENGINE_H -- GitLab