diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp
index 49705dc13a7067f5a45206b6aedb941e889555c9..3795f73688a38fd954aeb7d62406bfe4fd952f2c 100644
--- a/src/plugins/debugger/debuggerdialogs.cpp
+++ b/src/plugins/debugger/debuggerdialogs.cpp
@@ -570,8 +570,6 @@ bool StartExternalDialog::breakAtMain() const
     return m_ui->checkBoxBreakAtMain->isChecked();
 }
 
-
-
 ///////////////////////////////////////////////////////////////////////
 //
 // StartRemoteDialog
diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp
index 16483b8dfc941cf3daf17e5e1e9515788bd1d49c..ce67ada91698b376c7335fdd0c2f25aeb666c5b2 100644
--- a/src/plugins/debugger/debuggerplugin.cpp
+++ b/src/plugins/debugger/debuggerplugin.cpp
@@ -1585,6 +1585,10 @@ void DebuggerPluginPrivate::startExternalApplication()
     sp.breakAtMain = dlg.breakAtMain();
     if (!dlg.executableArguments().isEmpty())
         sp.processArgs = dlg.executableArguments().split(QLatin1Char(' '));
+    // Fixme: 1 of 3 testing hacks.
+    if (!sp.processArgs.isEmpty()
+        && (sp.processArgs.front() == _("@tcf@") || sp.processArgs.front() == _("@sym@")))
+        sp.toolChainType = ProjectExplorer::ToolChain::RVCT_ARMV5;
 
     startDebugger(m_debuggerRunControlFactory->create(sp));
 }
diff --git a/src/plugins/debugger/debuggerrunner.cpp b/src/plugins/debugger/debuggerrunner.cpp
index 9c38c145008d8b48d2df57e4ab8ec4e051ad4d9a..d642beeb84098f5c22d5fcb2c4daecce72edafcc 100644
--- a/src/plugins/debugger/debuggerrunner.cpp
+++ b/src/plugins/debugger/debuggerrunner.cpp
@@ -338,6 +338,10 @@ void DebuggerRunControl::createEngine(const DebuggerStartParameters &sp)
     else
         engineType = engineForToolChain(sp.toolChainType);
 
+    // Fixme: 1 of 3 testing hacks.
+    if (sp.processArgs.size() >= 5 && sp.processArgs.at(0) == _("@tcf@"))
+        engineType = GdbEngineType;
+
     if (engineType == NoEngineType
             && sp.startMode != AttachToRemote
             && !sp.executable.isEmpty())
diff --git a/src/plugins/debugger/gdb/gdb.pri b/src/plugins/debugger/gdb/gdb.pri
index ffe58619521656c9dcae0dd8cb1dea1877c71fec..e94b175bdf3b14719992db49e8be45fa2c1fad6d 100644
--- a/src/plugins/debugger/gdb/gdb.pri
+++ b/src/plugins/debugger/gdb/gdb.pri
@@ -10,12 +10,14 @@ HEADERS += \
     $$PWD/termgdbadapter.h \
     $$PWD/remotegdbserveradapter.h \
     $$PWD/trkgdbadapter.h \
+    $$PWD/tcftrkgdbadapter.h \
     $$PWD/s60debuggerbluetoothstarter.h \
     $$PWD/abstractgdbprocess.h \
     $$PWD/localgdbprocess.h \
     $$PWD/remotegdbprocess.h \
     $$PWD/remoteplaingdbadapter.h \
-    $$PWD/abstractplaingdbadapter.h
+    $$PWD/abstractplaingdbadapter.h \
+    $$PWD/symbian.h
 
 SOURCES += \
     $$PWD/gdbmi.cpp \
@@ -31,12 +33,14 @@ SOURCES += \
     $$PWD/termgdbadapter.cpp \
     $$PWD/remotegdbserveradapter.cpp \
     $$PWD/trkgdbadapter.cpp \
+    $$PWD/tcftrkgdbadapter.cpp \
     $$PWD/s60debuggerbluetoothstarter.cpp \
     $$PWD/abstractgdbprocess.cpp \
     $$PWD/localgdbprocess.cpp \
     $$PWD/remotegdbprocess.cpp \
     $$PWD/remoteplaingdbadapter.cpp \
-    $$PWD/abstractplaingdbadapter.cpp
+    $$PWD/abstractplaingdbadapter.cpp \
+    $$PWD/symbian.cpp
 
 FORMS +=  $$PWD/gdboptionspage.ui
 
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index c8a0ad106d705d6db5c5b7acfee02df1fe81a191..5605cb8088fd04ba67dbd5c9a861804bac53e1cc 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -43,6 +43,7 @@
 #include "remotegdbserveradapter.h"
 #include "remoteplaingdbadapter.h"
 #include "trkgdbadapter.h"
+#include "tcftrkgdbadapter.h"
 
 #include "watchutils.h"
 #include "debuggeractions.h"
@@ -1745,15 +1746,14 @@ AbstractGdbAdapter *GdbEngine::createAdapter()
         case ProjectExplorer::ToolChain::RVCT_ARMV6:
         case ProjectExplorer::ToolChain::RVCT_ARMV5_GNUPOC:
         case ProjectExplorer::ToolChain::GCCE_GNUPOC:
+            // fixme: 1 of 3 testing hacks
+            if (sp.processArgs.size() >= 5 && sp.processArgs.at(0) == _("@tcf@"))
+                return new TcfTrkGdbAdapter(this);
             return new TrkGdbAdapter(this);
         default:
             break;
     }
 
-    // @todo: remove testing hack
-    if (sp.processArgs.size() == 3 && sp.processArgs.at(0) == _("@sym@"))
-        return new TrkGdbAdapter(this);
-
     switch (sp.startMode) {
         case AttachCore:
             return new CoreGdbAdapter(this);
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index 4dd23a004ad4119361604f5c51eb571bf8b51dae..6a45096237b008a6e20ef3adde0abebafb84df80 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -99,6 +99,7 @@ private:
     friend class RemoteGdbServerAdapter;
     friend class RemotePlainGdbAdapter;
     friend class TrkGdbAdapter;
+    friend class TcfTrkGdbAdapter;
 
 private: ////////// General Interface //////////
 
diff --git a/src/plugins/debugger/gdb/symbian.cpp b/src/plugins/debugger/gdb/symbian.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9388fdf73389b865c8ae8913bd3e2004c4913a26
--- /dev/null
+++ b/src/plugins/debugger/gdb/symbian.cpp
@@ -0,0 +1,215 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 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 "symbian.h"
+#include <trkutils.h>
+
+#include <utils/qtcassert.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QTextStream>
+
+namespace Debugger {
+namespace Internal {
+
+///////////////////////////////////////////////////////////////////////////
+//
+// MemoryRange
+//
+///////////////////////////////////////////////////////////////////////////
+
+MemoryRange::MemoryRange(uint f, uint t)
+    : from(f), to(t)
+{
+    QTC_ASSERT(f <= t, qDebug() << "F: " << f << " T: " << t);
+}
+
+bool MemoryRange::intersects(const MemoryRange &other) const
+{
+    Q_UNUSED(other);
+    QTC_ASSERT(false, /**/);
+    return false; // FIXME
+}
+
+void MemoryRange::operator-=(const MemoryRange &other)
+{
+    if (from == 0 && to == 0)
+        return;
+    MEMORY_DEBUG("      SUB: "  << *this << " - " << other);
+    if (other.from <= from && to <= other.to) {
+        from = to = 0;
+        return;
+    }
+    if (other.from <= from && other.to <= to) {
+        from = qMax(from, other.to);
+        return;
+    }
+    if (from <= other.from && to <= other.to) {
+        to = qMin(other.from, to);
+        return;
+    }
+    // This would split the range.
+    QTC_ASSERT(false, qDebug() << "Memory::operator-() not handled for: "
+        << *this << " - " << other);
+}
+
+QDebug operator<<(QDebug d, const MemoryRange &range)
+{
+    return d << QString("[%1,%2] (size %3) ")
+        .arg(range.from, 0, 16).arg(range.to, 0, 16).arg(range.size());
+}
+
+namespace Symbian {
+
+static const char *registerNames[KnownRegisters] =
+{
+    "A1", "A2", "A3", "A4",
+    0, 0, 0, 0,
+    0, 0, 0, "AP",
+    "IP", "SP", "LR", "PC",
+    "PSTrk", 0, 0, 0,
+    0, 0, 0, 0,
+    0, "PSGdb"
+};
+
+const char *registerName(int i)
+{
+    return registerNames[i];
+}
+
+QByteArray dumpRegister(uint n, uint value)
+{
+    QByteArray ba;
+    ba += ' ';
+    if (n < KnownRegisters && registerNames[n]) {
+        ba += registerNames[n];
+    } else {
+        ba += '#';
+        ba += QByteArray::number(n);
+    }
+    ba += '=';
+    ba += trk::hexxNumber(value);
+    return ba;
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Snapshot
+//
+///////////////////////////////////////////////////////////////////////////
+
+void Snapshot::reset()
+{
+    for (Memory::Iterator it = memory.begin(); it != memory.end(); ++it) {
+        if (isReadOnly(it.key())) {
+            MEMORY_DEBUG("KEEPING READ-ONLY RANGE" << it.key());
+        } else {
+            it = memory.erase(it);
+        }
+    }
+    for (int i = 0; i < RegisterCount; ++i)
+        registers[i] = 0;
+    registerValid = false;
+    wantedMemory = MemoryRange();
+    lineFromAddress = 0;
+    lineToAddress = 0;
+}
+
+void Snapshot::fullReset()
+{
+    memory.clear();
+    reset();
+}
+
+void Snapshot::insertMemory(const MemoryRange &range, const QByteArray &ba)
+{
+    QTC_ASSERT(range.size() == uint(ba.size()),
+        qDebug() << "RANGE: " << range << " BA SIZE: " << ba.size(); return);
+
+    MEMORY_DEBUG("INSERT: " << range);
+    // Try to combine with existing chunk.
+    Snapshot::Memory::iterator it = memory.begin();
+    Snapshot::Memory::iterator et = memory.end();
+    for ( ; it != et; ++it) {
+        if (range.from == it.key().to) {
+            MEMORY_DEBUG("COMBINING " << it.key() << " AND " << range);
+            QByteArray data = *it;
+            data.append(ba);
+            const MemoryRange res(it.key().from, range.to);
+            memory.remove(it.key());
+            MEMORY_DEBUG(" TO(1)  " << res);
+            insertMemory(res, data);
+            return;
+        }
+        if (it.key().from == range.to) {
+            MEMORY_DEBUG("COMBINING " << range << " AND " << it.key());
+            QByteArray data = ba;
+            data.append(*it);
+            const MemoryRange res(range.from, it.key().to);
+            memory.remove(it.key());
+            MEMORY_DEBUG(" TO(2)  " << res);
+            insertMemory(res, data);
+            return;
+        }
+    }
+
+    // Not combinable, add chunk.
+    memory.insert(range, ba);
+}
+
+QString Snapshot::toString() const
+{
+    typedef QMap<MemoryRange, QByteArray>::const_iterator MemCacheConstIt;
+    QString rc;
+    QTextStream str(&rc);
+    str << "Register valid " << registerValid << ' ';
+    for (int i = 0; i < RegisterCount; i++) {
+        if (i)
+            str << ", ";
+        str << " R" << i << "=0x";
+        str.setIntegerBase(16);
+        str << registers[i];
+        str.setIntegerBase(10);
+    }
+    str << '\n';
+    // For next step.
+    if (!memory.isEmpty()) {
+        str.setIntegerBase(16);
+        str << "Memory:\n";
+        const MemCacheConstIt mcend = memory.constEnd();
+        for (MemCacheConstIt it = memory.constBegin(); it != mcend; ++it)
+            str << "  0x" << it.key().from << " - 0x" << it.key().to << '\n';
+    }
+    return rc;
+}
+
+} // namespace Symbian
+} // namespace Internal
+} // namespace Debugger
diff --git a/src/plugins/debugger/gdb/symbian.h b/src/plugins/debugger/gdb/symbian.h
new file mode 100644
index 0000000000000000000000000000000000000000..63e3c4e48f1a2fb186822787af440f1de4d28d50
--- /dev/null
+++ b/src/plugins/debugger/gdb/symbian.h
@@ -0,0 +1,145 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 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 SYMBIANUTILS_H
+#define SYMBIANUTILS_H
+
+#include <QtCore/QMap>
+#include <QtCore/QByteArray>
+#include <QtCore/QMetaType>
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+QT_END_NAMESPACE
+
+//#define DEBUG_MEMORY  1
+#if DEBUG_MEMORY
+#   define MEMORY_DEBUG(s) qDebug() << s
+#else
+#   define MEMORY_DEBUG(s)
+#endif
+#define MEMORY_DEBUGX(s) qDebug() << s
+
+namespace Debugger {
+namespace Internal {
+
+struct GdbResult {
+    QByteArray data;
+};
+
+struct MemoryRange
+{
+    MemoryRange() : from(0), to(0) {}
+    MemoryRange(uint f, uint t);
+    void operator-=(const MemoryRange &other);
+    bool intersects(const MemoryRange &other) const;
+    quint64 hash() const { return (quint64(from) << 32) + to; }
+    bool operator==(const MemoryRange &other) const { return hash() == other.hash(); }
+    bool operator<(const MemoryRange &other) const { return hash() < other.hash(); }
+    uint size() const { return to - from; }
+
+    uint from; // Inclusive.
+    uint to;   // Exclusive.
+};
+
+QDebug operator<<(QDebug d, const MemoryRange &range);
+
+namespace Symbian {
+
+enum CodeMode
+{
+    ArmMode = 0,
+    ThumbMode,
+};
+
+enum TargetConstants
+{
+    RegisterCount = 17,
+    RegisterSP = 13, // Stack Pointer
+    RegisterLR = 14, // Return address
+    RegisterPC = 15, // Program counter
+    RegisterPSGdb = 25, // gdb's view of the world
+    RegisterPSTrk = 16, // TRK's view of the world
+
+    MemoryChunkSize = 256
+};
+
+enum { KnownRegisters = RegisterPSGdb + 1};
+
+const char *registerName(int i);
+QByteArray dumpRegister(uint n, uint value);
+
+inline bool isReadOnly(const MemoryRange &mr)
+{
+    return  mr.from >= 0x70000000 && mr.to < 0x80000000;
+}
+
+struct Snapshot
+{
+    Snapshot() { reset(); }
+
+    void reset(); // Leaves read-only memory cache alive.
+    void fullReset(); // Also removes read-only memory cache.
+    void insertMemory(const MemoryRange &range, const QByteArray &ba);
+    QString toString() const;
+
+    uint registers[RegisterCount];
+    bool registerValid;
+    typedef QMap<MemoryRange, QByteArray> Memory;
+    Memory memory;
+
+    // Current state.
+    MemoryRange wantedMemory;
+
+    // For next step.
+    uint lineFromAddress;
+    uint lineToAddress;
+    bool stepOver;
+};
+
+struct Breakpoint
+{
+    Breakpoint(uint offset_ = 0)
+    {
+        number = 0;
+        offset = offset_;
+        mode = ArmMode;
+    }
+    uint offset;
+    ushort number;
+    CodeMode mode;
+};
+
+} // namespace Symbian
+} // namespace Internal
+} // namespace Debugger
+
+Q_DECLARE_METATYPE(Debugger::Internal::MemoryRange);
+
+#endif // SYMBIANUTILS_H
diff --git a/src/plugins/debugger/gdb/tcftrkgdbadapter.cpp b/src/plugins/debugger/gdb/tcftrkgdbadapter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e6ab8354933d65046734c464b50731c75e07520c
--- /dev/null
+++ b/src/plugins/debugger/gdb/tcftrkgdbadapter.cpp
@@ -0,0 +1,1522 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 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 "tcftrkgdbadapter.h"
+
+#include "tcftrkdevice.h"
+#include "trkutils.h"
+
+#include "registerhandler.h"
+#include "threadshandler.h"
+#include "debuggeractions.h"
+#include "debuggerstringutils.h"
+#include "watchutils.h"
+#ifndef STANDALONE_RUNNER
+#include "gdbengine.h"
+#endif
+
+#include <utils/qtcassert.h>
+#include <utils/savedaction.h>
+
+#include <QtCore/QTimer>
+#include <QtCore/QDir>
+#include <QtNetwork/QTcpServer>
+#include <QtNetwork/QTcpSocket>
+
+#ifdef Q_OS_WIN
+#  include <windows.h>
+#else
+#  include <sys/types.h>
+#  include <unistd.h>
+#endif
+
+#define CB(callback) \
+    static_cast<GdbEngine::AdapterCallback>(&TcfTrkGdbAdapter::callback), \
+    STRINGIFY(callback)
+
+enum { debug = 0 };
+
+static void appendRegister(QByteArray *ba, uint regno, uint value)
+{
+    ba->append(trk::hexNumber(regno, 2));
+    ba->append(':');
+    ba->append(trk::hexNumber(trk::swapEndian(value), 8));
+    ba->append(';');
+}
+
+// Register names used by the 'SimpleRegister' service
+static const char* tcfTrkSimpleRegisterNamesC[] =
+{"R0",  "R1", "R2",  "R3",
+ "R4",  "R5", "R6",  "R7",
+ "R8",  "R9", "R10", "R11",
+ "R12", "SP", "LR",  "PC",
+ "CPSR"};
+
+namespace Debugger {
+namespace Internal {
+using namespace Symbian;
+
+static inline QString startMsg(const trk::Session &session)
+{
+    return TcfTrkGdbAdapter::tr("Process started, PID: 0x%1, thread id: 0x%2, "
+       "code segment: 0x%3, data segment: 0x%4.")
+         .arg(session.pid, 0, 16).arg(session.tid, 0, 16)
+         .arg(session.codeseg, 0, 16).arg(session.dataseg, 0, 16);
+}
+
+/* -------------- TcfTrkGdbAdapter:
+ * Startup-sequence:
+ *  - startAdapter connects both sockets/devices
+ *  - In the TCF Locator Event, gdb is started and adapterStarted is emitted.
+ *  - Engine calls startInferior(), which starts the process.
+ *  - Initial TCF module load suspended event is emitted (process is suspended).
+ *    In the event handler, gdb is connected to the remote target. In the
+ *    gdb answer to conntect remote, inferiorStartPrepared() is emitted.
+ *  - Engine sets up breakpoints,etc and calls inferiorStartPhase2(), which
+ *    resumes the suspended TCF process via gdb 'continue'.
+ */
+
+TcfTrkGdbAdapter::TcfTrkGdbAdapter(GdbEngine *engine) :
+    AbstractGdbAdapter(engine),
+    m_running(false),
+    m_trkDevice(new tcftrk::TcfTrkDevice(this)),
+    m_gdbAckMode(true),
+    m_uid(0),
+    m_verbose(0),
+    m_firstModuleResumableEvent(false)
+{
+    m_bufferedMemoryRead = true;
+    // Disable buffering if gdb's dcache is used.
+    m_bufferedMemoryRead = false;
+
+    m_gdbServer = 0;
+    m_gdbConnection = 0;
+    m_snapshot.reset();
+#ifdef Q_OS_WIN
+    const DWORD portOffset = GetCurrentProcessId() % 100;
+#else
+    const uid_t portOffset = getuid();
+#endif
+    m_gdbServerName = _("127.0.0.1:%1").arg(2222 + portOffset);
+
+    setVerbose(theDebuggerBoolSetting(VerboseLog) ? 1 : 0);
+
+    connect(theDebuggerAction(VerboseLog), SIGNAL(valueChanged(QVariant)),
+        this, SLOT(setVerbose(QVariant)));
+    connect(m_trkDevice, SIGNAL(error(QString)), this, SLOT(tcftrkDeviceError(QString)));
+    connect(m_trkDevice, SIGNAL(logMessage(QString)), this, SLOT(trkLogMessage(QString)));
+    connect(m_trkDevice, SIGNAL(tcfEvent(tcftrk::TcfTrkEvent)), this,
+            SLOT(tcftrkEvent(tcftrk::TcfTrkEvent)));
+
+    // Set register mappings
+    const int registerCount = sizeof(tcfTrkSimpleRegisterNamesC)/sizeof(const char*);
+    QVector<QByteArray> registerNames;
+    registerNames.reserve(registerCount);
+    for (int i = 0; i < registerCount; i++)
+        registerNames.push_back(QByteArray(tcfTrkSimpleRegisterNamesC[i]));
+    m_trkDevice->setRegisterNames(registerNames);
+}
+
+TcfTrkGdbAdapter::~TcfTrkGdbAdapter()
+{
+    cleanup();
+    logMessage("Shutting down.\n");
+}
+
+void TcfTrkGdbAdapter::setVerbose(const QVariant &value)
+{
+    setVerbose(value.toInt());
+}
+
+void TcfTrkGdbAdapter::setVerbose(int verbose)
+{
+    if (debug)
+        qDebug("TcfTrkGdbAdapter::setVerbose %d", verbose);
+    m_verbose = verbose;
+    m_trkDevice->setVerbose(m_verbose);
+}
+
+void TcfTrkGdbAdapter::trkLogMessage(const QString &msg)
+{
+    logMessage(_("TRK ") + msg);
+}
+
+void TcfTrkGdbAdapter::setGdbServerName(const QString &name)
+{
+    m_gdbServerName = name;
+}
+
+QString TcfTrkGdbAdapter::gdbServerIP() const
+{
+    int pos = m_gdbServerName.indexOf(':');
+    if (pos == -1)
+        return m_gdbServerName;
+    return m_gdbServerName.left(pos);
+}
+
+uint TcfTrkGdbAdapter::gdbServerPort() const
+{
+    int pos = m_gdbServerName.indexOf(':');
+    if (pos == -1)
+        return 0;
+    return m_gdbServerName.mid(pos + 1).toUInt();
+}
+
+void TcfTrkGdbAdapter::handleTcfTrkRunControlModuleLoadContextSuspendedEvent(const tcftrk::TcfTrkRunControlModuleLoadContextSuspendedEvent &se)
+{
+    const tcftrk::ModuleLoadEventInfo &minfo = se.info();
+    // Register in session, keep modules and libraries in sync.
+    const QString moduleName = QString::fromUtf8(minfo.name);
+    if (true || minfo.loaded) { // TODO: Preliminary TCF Trk Versions always have Loaded=false?
+        m_session.modules.push_back(moduleName);
+        trk::Library library;
+        library.name = minfo.name;
+        library.codeseg = minfo.codeAddress;
+        library.dataseg = minfo.dataAddress;
+        m_session.libraries.push_back(library);
+    } else {
+        const int index = m_session.modules.indexOf(moduleName);
+        if (index != -1) {
+            m_session.modules.removeAt(index);
+            m_session.libraries.removeAt(index);
+        } else {
+            // Might happen with preliminary version of TCF TRK.
+            qWarning("Received unload for module '%s' for which no load was received.",
+                     qPrintable(moduleName));
+        }
+
+    }
+    // Handle resume.
+    if (se.info().requireResume) {
+        // If it is the first, resumable load event (.exe), make
+        // gdb connect to remote target and resume in startInferior2(),
+        if (m_firstModuleResumableEvent) {
+            m_firstModuleResumableEvent = false;
+            m_tcfProcessId = se.id();
+            m_session.codeseg = minfo.codeAddress;
+            m_session.dataseg = minfo.dataAddress;
+            logMessage(startMsg(m_session));
+            const QByteArray symbolFile = m_symbolFile.toLocal8Bit();
+            if (symbolFile.isEmpty()) {
+                logMessage(_("WARNING: No symbol file available."), LogWarning);
+            } else {
+                // Does not seem to be necessary anymore.
+                // FIXME: Startup sequence can be streamlined now as we do not
+                // have to wait for the TRK startup to learn the load address.
+                //m_engine->postCommand("add-symbol-file \"" + symbolFile + "\" "
+                //    + QByteArray::number(m_session.codeseg));
+                m_engine->postCommand("symbol-file \"" + symbolFile + "\"");
+            }
+            m_engine->postCommand("set breakpoint always-inserted on");
+            m_engine->postCommand("set breakpoint auto-hw on");
+            m_engine->postCommand("set trust-readonly-sections on"); // No difference?
+            m_engine->postCommand("set displaced-stepping on"); // No difference?
+            //m_engine->postCommand("set remotelogfile /tmp/gdb-remotelog");
+            //m_engine->postCommand("set debug remote 1"); // FIXME: Make an option.
+            m_engine->postCommand("set mem inaccessible-by-default");
+            m_engine->postCommand("mem 0x00400000 0x70000000 cache");
+            m_engine->postCommand("mem 0x70000000 0x80000000 cache ro");
+            // FIXME: replace with  stack-cache for newer gdb?
+            m_engine->postCommand("set remotecache on");  // "info dcache" to check
+            m_engine->postCommand("target remote " + gdbServerName().toLatin1(),
+                                  CB(handleTargetRemote));
+            if (debug)
+                qDebug() << "Initial module load suspended: " << m_session.toString();
+        } else {
+            // Consecutive module load suspended: (not observed yet): Just continue
+            m_trkDevice->sendRunControlResumeCommand(TcfTrkCallback(), se.id());
+        }
+    }
+}
+
+void TcfTrkGdbAdapter::handleTargetRemote(const GdbResponse &record)
+{
+    QTC_ASSERT(state() == InferiorStarting, qDebug() << state());
+    if (record.resultClass == GdbResultDone) {
+        setState(InferiorStopped);
+        emit inferiorPrepared();
+        if (debug)
+            qDebug() << "handleTargetRemote" << m_session.toString();
+    } else {
+        QString msg = tr("Connecting to TRK server adapter failed:\n")
+            + QString::fromLocal8Bit(record.data.findChild("msg").data());
+        emit inferiorStartFailed(msg);
+    }
+}
+
+void TcfTrkGdbAdapter::tcftrkEvent(const tcftrk::TcfTrkEvent &e)
+{
+    logMessage(e.toString());
+
+    switch (e.type()) {
+    case tcftrk::TcfTrkEvent::LocatorHello:
+        startGdb(); // Commands are only accepted after hello
+        break;
+    case tcftrk::TcfTrkEvent::RunControlModuleLoadSuspended:
+        handleTcfTrkRunControlModuleLoadContextSuspendedEvent(static_cast<const tcftrk::TcfTrkRunControlModuleLoadContextSuspendedEvent &>(e));
+        break;
+    case tcftrk::TcfTrkEvent::RunControlContextAdded:
+        foreach(const tcftrk::RunControlContext &rc, static_cast<const tcftrk::TcfTrkRunControlContextAddedEvent &>(e).contexts())
+            if (rc.type() == tcftrk::RunControlContext::Thread) {
+                m_session.threads.push_back(rc.threadId());
+                if (m_session.tid == 0)
+                    m_session.tid = rc.threadId();
+            }
+        break;
+    case tcftrk::TcfTrkEvent::RunControlContextRemoved:
+        foreach(const QByteArray &id, static_cast<const tcftrk::TcfTrkRunControlContextRemovedEvent &>(e).ids())
+            switch (tcftrk::RunControlContext::typeFromTcfId(id)) {
+            case tcftrk::RunControlContext::Thread:
+                m_session.threads.removeAll(tcftrk::RunControlContext::threadIdFromTcdfId(id));
+                break;
+            case tcftrk::RunControlContext::Process:
+                sendGdbServerMessage("W00", "Process exited");
+                break;
+        }
+        break;
+    case tcftrk::TcfTrkEvent::RunControlSuspended: {
+            const tcftrk::TcfTrkRunControlContextSuspendedEvent &se = static_cast<const tcftrk::TcfTrkRunControlContextSuspendedEvent &>(e);
+            showMessage(QString::fromLatin1("RESET SNAPSHOT (NOTIFY STOPPED: '%1')").arg(QString::fromLatin1(se.reasonID())));
+            m_snapshot.reset();
+            // Update registers first, then report stopped
+            m_running = false;
+            m_trkDevice->sendRegistersGetMRangeCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleAndReportReadRegistersAfterStop),
+                                                       m_tcfProcessId, 0,
+                                                       Symbian::RegisterCount);
+            // TODO:mov
+    }
+        break;
+    default:
+        break;
+    }
+}
+
+void TcfTrkGdbAdapter::startGdb()
+{
+    QStringList gdbArgs;
+    gdbArgs.append(QLatin1String("--nx")); // Do not read .gdbinit file
+    if (!m_engine->startGdb(gdbArgs, QString(), QString())) {
+        cleanup();
+        return;
+    }
+    emit adapterStarted();
+}
+
+void TcfTrkGdbAdapter::tcftrkDeviceError(const QString  &errorString)
+{
+    logMessage(errorString);
+    if (state() == AdapterStarting) {
+        emit adapterStartFailed(errorString, QString());
+    } else {
+        emit adapterCrashed(errorString);
+    }
+}
+
+void TcfTrkGdbAdapter::logMessage(const QString &msg, int channel)
+{
+    if (m_verbose || channel != LogDebug)
+        showMessage(msg, channel);
+    if (debug)
+        qDebug("%s", qPrintable(msg));
+}
+
+//
+// Gdb
+//
+void TcfTrkGdbAdapter::handleGdbConnection()
+{
+    logMessage("HANDLING GDB CONNECTION");
+    QTC_ASSERT(m_gdbConnection == 0, /**/);
+    m_gdbConnection = m_gdbServer->nextPendingConnection();
+    QTC_ASSERT(m_gdbConnection, return);
+    connect(m_gdbConnection, SIGNAL(disconnected()),
+            m_gdbConnection, SLOT(deleteLater()));
+    connect(m_gdbConnection, SIGNAL(readyRead()),
+            this, SLOT(readGdbServerCommand()));
+}
+
+static inline QString msgGdbPacket(const QString &p)
+{
+    return QLatin1String("gdb:                              ") + p;
+}
+
+void TcfTrkGdbAdapter::readGdbServerCommand()
+{
+    QTC_ASSERT(m_gdbConnection, return);
+    QByteArray packet = m_gdbConnection->readAll();
+    m_gdbReadBuffer.append(packet);
+
+    logMessage("gdb: -> " + currentTime() + ' ' + QString::fromAscii(packet));
+    if (packet != m_gdbReadBuffer)
+        logMessage("buffer: " + m_gdbReadBuffer);
+
+    QByteArray &ba = m_gdbReadBuffer;
+    while (ba.size()) {
+        char code = ba.at(0);
+        ba = ba.mid(1);
+
+        if (code == '+') {
+            //logMessage("ACK");
+            continue;
+        }
+
+        if (code == '-') {
+            logMessage("NAK: Retransmission requested", LogError);
+            // This seems too harsh.
+            //emit adapterCrashed("Communication problem encountered.");
+            continue;
+        }
+
+        if (code == char(0x03)) {
+            logMessage("INTERRUPT RECEIVED");
+            interruptInferior();
+            continue;
+        }
+
+        if (code != '$') {
+            logMessage("Broken package (2) " + quoteUnprintableLatin1(ba)
+                + trk::hexNumber(code), LogError);
+            continue;
+        }
+
+        int pos = ba.indexOf('#');
+        if (pos == -1) {
+            logMessage("Invalid checksum format in "
+                + quoteUnprintableLatin1(ba), LogError);
+            continue;
+        }
+
+        bool ok = false;
+        uint checkSum = ba.mid(pos + 1, 2).toUInt(&ok, 16);
+        if (!ok) {
+            logMessage("Invalid checksum format 2 in "
+                + quoteUnprintableLatin1(ba), LogError);
+            return;
+        }
+
+        //logMessage(QString("Packet checksum: %1").arg(checkSum));
+        trk::byte sum = 0;
+        for (int i = 0; i < pos; ++i)
+            sum += ba.at(i);
+
+        if (sum != checkSum) {
+            logMessage(QString("ERROR: Packet checksum wrong: %1 %2 in "
+                + quoteUnprintableLatin1(ba)).arg(checkSum).arg(sum), LogError);
+        }
+
+        QByteArray cmd = ba.left(pos);
+        ba.remove(0, pos + 3);
+        handleGdbServerCommand(cmd);
+    }
+}
+
+bool TcfTrkGdbAdapter::sendGdbServerPacket(const QByteArray &packet, bool doFlush)
+{
+    if (!m_gdbConnection) {
+        logMessage(_("Cannot write to gdb: No connection (%1)")
+            .arg(_(packet)), LogError);
+        return false;
+    }
+    if (m_gdbConnection->state() != QAbstractSocket::ConnectedState) {
+        logMessage(_("Cannot write to gdb: Not connected (%1)")
+            .arg(_(packet)), LogError);
+        return false;
+    }
+    if (m_gdbConnection->write(packet) == -1) {
+        logMessage(_("Cannot write to gdb: %1 (%2)")
+            .arg(m_gdbConnection->errorString()).arg(_(packet)), LogError);
+        return false;
+    }
+    if (doFlush)
+        m_gdbConnection->flush();
+    return true;
+}
+
+void TcfTrkGdbAdapter::sendGdbServerAck()
+{
+    if (!m_gdbAckMode)
+        return;
+    logMessage("gdb: <- +");
+    sendGdbServerPacket(QByteArray(1, '+'), false);
+}
+
+void TcfTrkGdbAdapter::sendGdbServerMessage(const QByteArray &msg, const QByteArray &logNote)
+{
+    trk::byte sum = 0;
+    for (int i = 0; i != msg.size(); ++i)
+        sum += msg.at(i);
+
+    char checkSum[30];
+    qsnprintf(checkSum, sizeof(checkSum) - 1, "%02x ", sum);
+
+    //logMessage(QString("Packet checksum: %1").arg(sum));
+
+    QByteArray packet;
+    packet.append('$');
+    packet.append(msg);
+    packet.append('#');
+    packet.append(checkSum);
+    int pad = qMax(0, 24 - packet.size());
+    logMessage("gdb: <- " + currentTime() + ' ' + packet + QByteArray(pad, ' ') + logNote);
+    sendGdbServerPacket(packet, true);
+}
+
+static QByteArray msgStepRangeReceived(unsigned from, unsigned to, bool over)
+{
+    QByteArray rc = "Stepping range received for step ";
+    rc += over ? "over" : "into";
+    rc += " (0x";
+    rc += QByteArray::number(from, 16);
+    rc += " to 0x";
+    rc += QByteArray::number(to, 16);
+    rc += ')';
+    return rc;
+}
+
+// Read address/length off a memory command like m,X
+static QPair<quint64, unsigned> readMemoryAddressLength(const QByteArray &cmd)
+{
+    QPair<quint64, unsigned> rc(0, 0);
+    const int pos = cmd.indexOf(',');
+    if (pos == -1)
+        return rc;
+    bool ok;
+    rc.first = cmd.mid(1, pos - 1).toULongLong(&ok, 16);
+    if (!ok)
+        return rc;
+    rc.second = cmd.mid(pos + 1).toUInt(&ok, 16);
+    if (!ok)
+        rc.first = 0;
+    return rc;
+}
+
+void TcfTrkGdbAdapter::handleGdbServerCommand(const QByteArray &cmd)
+{
+    if (debug)
+        qDebug("handleGdbServerCommand: %s", cmd.constData());
+    // http://sourceware.org/gdb/current/onlinedocs/gdb_34.html
+    if (0) {}
+    else if (cmd == "!") {
+        sendGdbServerAck();
+        //sendGdbServerMessage("", "extended mode not enabled");
+        sendGdbServerMessage("OK", "extended mode enabled");
+    }
+
+    else if (cmd.startsWith('?')) {
+        logMessage(msgGdbPacket(QLatin1String("Query halted")));
+        // Indicate the reason the target halted.
+        // The reply is the same as for step and continue.
+        sendGdbServerAck();
+        // The command below will trigger fetching a stack trace while
+        // the process does not seem to be fully functional. Most notably
+        // the PC points to a 0x9..., which is not in "our" range
+        //sendGdbServerMessage("T05library:r;", "target halted (library load)");
+        //sendGdbServerMessage("S05", "target halted (trap)");
+        sendGdbServerMessage("S00", "target halted (trap)");
+        //sendGdbServerMessage("O" + QByteArray("Starting...").toHex());
+    }
+
+    else if (cmd == "c") {
+        logMessage(msgGdbPacket(QLatin1String("Continue")));
+        sendGdbServerAck();
+        sendTrkContinue();
+    }
+
+    else if (cmd.startsWith('C')) {
+        logMessage(msgGdbPacket(QLatin1String("Continue with signal")));
+        // C sig[;addr] Continue with signal sig (hex signal number)
+        //Reply: See section D.3 Stop Reply Packets, for the reply specifications.
+        sendGdbServerAck();
+        bool ok = false;
+        const uint signalNumber = cmd.mid(1).toUInt(&ok, 16);
+        m_trkDevice->sendRunControlResumeCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleSignalContinue),
+                                                 mainThreadContextId(),
+                                                 QVariant(signalNumber));
+    }
+
+    else if (cmd.startsWith('D')) {
+        sendGdbServerAck();
+        sendGdbServerMessage("OK", "shutting down");
+    }
+
+    else if (cmd == "g") {
+        // Read general registers.
+        if (m_snapshot.registerValid) {
+            logMessage(msgGdbPacket(QLatin1String("Read registers")));
+            sendGdbServerAck();
+            reportRegisters();
+        } else {
+            sendGdbServerAck();
+            m_trkDevice->sendRegistersGetMRangeCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleAndReportReadRegisters),
+                                                       m_tcfProcessId, 0,
+                                                       Symbian::RegisterCount);
+        }
+    }
+
+    else if (cmd == "gg") {
+        // Force re-reading general registers for debugging purpose.
+        sendGdbServerAck();
+        m_snapshot.registerValid = false;
+        m_trkDevice->sendRegistersGetMRangeCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleAndReportReadRegisters),
+                                                   m_tcfProcessId, 0,
+                                                   Symbian::RegisterCount);
+    }
+
+    else if (cmd.startsWith("salstep,")) {
+        // Receive address range for current line for future use when stepping.
+        sendGdbServerAck();
+        int pos = cmd.indexOf(',', 8);
+        m_snapshot.lineFromAddress = cmd.mid(8, pos - 8).toUInt(0, 16);
+        m_snapshot.lineToAddress = cmd.mid(pos + 1).toUInt(0, 16);
+        m_snapshot.stepOver = false;
+        sendGdbServerMessage("", msgStepRangeReceived(m_snapshot.lineFromAddress, m_snapshot.lineToAddress, m_snapshot.stepOver));
+    }
+
+    else if (cmd.startsWith("salnext,")) {
+        // Receive address range for current line for future use when stepping.
+        sendGdbServerAck();
+        const int pos = cmd.indexOf(',', 8);
+        m_snapshot.lineFromAddress = cmd.mid(8, pos - 8).toUInt(0, 16);
+        m_snapshot.lineToAddress = cmd.mid(pos + 1).toUInt(0, 16);
+        m_snapshot.stepOver = true;
+        sendGdbServerMessage("", msgStepRangeReceived(m_snapshot.lineFromAddress, m_snapshot.lineToAddress, m_snapshot.stepOver));
+    }
+
+    else if (cmd.startsWith("Hc")) {
+        logMessage(msgGdbPacket(QLatin1String("Set thread & continue")));
+        // Set thread for subsequent operations (`m', `M', `g', `G', et.al.).
+        // for step and continue operations
+        //$Hc-1#09
+        sendGdbServerAck();
+        sendGdbServerMessage("OK", "Set current thread for step & continue");
+    }
+
+    else if (cmd.startsWith("Hg")) {
+        logMessage(msgGdbPacket(QLatin1String("Set thread")));
+        // Set thread for subsequent operations (`m', `M', `g', `G', et.al.).
+        // for 'other operations.  0 - any thread
+        //$Hg0#df
+        sendGdbServerAck();
+        m_session.currentThread = cmd.mid(2).toUInt(0, 16);
+        sendGdbServerMessage("OK", "Set current thread "
+            + QByteArray::number(m_session.currentThread));
+    }
+
+    else if (cmd == "k" || cmd.startsWith("vKill")) {
+        // Kill inferior process
+        logMessage(msgGdbPacket(QLatin1String("kill")));
+        m_trkDevice->sendProcessTerminateCommand(TcfTrkCallback(),
+                                                 m_tcfProcessId);
+    }
+
+    else if (cmd.startsWith('m')) {
+        logMessage(msgGdbPacket(QLatin1String("Read memory")));
+        // m addr,length
+        sendGdbServerAck();
+        const QPair<quint64, unsigned> addrLength = readMemoryAddressLength(cmd);
+        if (addrLength.first && addrLength.second) {
+            readMemory(addrLength.first, addrLength.second, m_bufferedMemoryRead);
+        } else {
+            sendGdbServerMessage("E20", "Error " + cmd);
+        }
+    }
+
+    else if (cmd.startsWith('X'))  {
+        const int dataPos = cmd.indexOf(':');
+        if (dataPos == -1) {
+            sendGdbServerMessage("E20", "Error (colon expected) " + cmd);
+            return;
+        }
+        const QPair<quint64, unsigned> addrLength = readMemoryAddressLength(cmd.left(dataPos));
+        if (addrLength.first == 0) {
+            sendGdbServerMessage("E20", "Error (address = 0) " + cmd);
+            return;
+        }
+        // Requests with len=0 are apparently used to probe writing.
+        if (addrLength.second == 0) {
+            sendGdbServerMessage("OK", "Probe memory write at 0x" + QByteArray::number(addrLength.first, 16));
+            return;
+        }
+        // Data appear to be plain binary.
+        const QByteArray data = cmd.mid(dataPos + 1);
+        if (addrLength.second != unsigned(data.size())) {
+            sendGdbServerMessage("E20", "Data length mismatch " + cmd);
+            return;
+        }
+        logMessage(QString::fromLatin1("Writing %1 bytes from 0x%2: %3").
+                   arg(addrLength.second).arg(addrLength.first, 0, 16).
+                   arg(QString::fromAscii(data.toHex())));
+        m_trkDevice->sendMemorySetCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleWriteMemory),
+                                          m_tcfProcessId,
+                                          addrLength.first, data);
+    }
+
+    else if (cmd.startsWith('p')) {
+        logMessage(msgGdbPacket(QLatin1String("read register")));
+        // 0xf == current instruction pointer?
+        //sendGdbServerMessage("0000", "current IP");
+        sendGdbServerAck();
+        bool ok = false;
+        const uint registerNumber = cmd.mid(1).toUInt(&ok, 16);
+        if (m_snapshot.registerValid) {
+            QByteArray logMsg = "Read Register";
+            if (registerNumber == RegisterPSGdb) {
+                QByteArray ba;
+                appendInt(&ba, m_snapshot.registers[RegisterPSTrk], trk::LittleEndian);
+                logMsg += dumpRegister(registerNumber, m_snapshot.registers[RegisterPSTrk]);
+                sendGdbServerMessage(ba.toHex(), logMsg);
+            } else if (registerNumber < 16) {
+                QByteArray ba;
+                appendInt(&ba, m_snapshot.registers[registerNumber], trk::LittleEndian);
+                logMsg += dumpRegister(registerNumber, m_snapshot.registers[registerNumber]);
+                sendGdbServerMessage(ba.toHex(), logMsg);
+            } else {
+                sendGdbServerMessage("0000", "read single unknown register #"
+                    + QByteArray::number(registerNumber));
+                //sendGdbServerMessage("E01", "read single unknown register");
+            }
+        } else {
+            //qDebug() << "Fetching single register";
+            m_trkDevice->sendRegistersGetMRangeCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleAndReportReadRegistersAfterStop),
+                                                       m_tcfProcessId, registerNumber, 1);
+        }
+    }
+
+    else if (cmd.startsWith('P')) {
+        logMessage(msgGdbPacket(QLatin1String("write register")));
+        // $Pe=70f96678#d3
+        sendGdbServerAck();
+        int pos = cmd.indexOf('=');
+        QByteArray regName = cmd.mid(1, pos - 1);
+        QByteArray valueName = cmd.mid(pos + 1);
+        bool ok = false;
+        const uint registerNumber = regName.toUInt(&ok, 16);
+        const uint value = trk::swapEndian(valueName.toUInt(&ok, 16));
+        // FIXME: Assume all goes well.
+        m_snapshot.registers[registerNumber] = value;
+        logMessage(QString::fromLatin1("Setting register #%1 to 0x%2").arg(registerNumber).arg(value, 0, 16));
+        m_trkDevice->sendRegistersSetCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleWriteRegister),
+                                             m_tcfProcessId, registerNumber, value, QVariant(registerNumber));
+        // Note that App TRK refuses to write registers 13 and 14
+    }
+
+    else if (cmd == "qAttached") {
+        //$qAttached#8f
+        // 1: attached to an existing process
+        // 0: created a new process
+        sendGdbServerAck();
+        sendGdbServerMessage(QByteArray(1, '0'), "new process created");
+    }
+
+    else if (cmd.startsWith("qC")) {
+        logMessage(msgGdbPacket(QLatin1String("query thread id")));
+        // Return the current thread ID
+        //$qC#b4
+        QTC_ASSERT(!m_session.threads.isEmpty(), return)
+        sendGdbServerAck();
+        sendGdbServerMessage("QC" + QByteArray::number(m_session.threads.front(), 16));
+    }
+
+    else if (cmd.startsWith("qSupported")) {
+        //$qSupported#37
+        //$qSupported:multiprocess+#c6
+        //logMessage("Handling 'qSupported'");
+        sendGdbServerAck();
+        sendGdbServerMessage(
+            "PacketSize=7cf;"
+            "QPassSignals+;"
+            "QStartNoAckMode+;"
+            "qXfer:libraries:read+;"
+            //"qXfer:auxv:read+;"
+            "qXfer:features:read+");
+    }
+
+    else if (cmd.startsWith("qThreadExtraInfo")) {
+        // $qThreadExtraInfo,1f9#55
+        sendGdbServerAck();
+        sendGdbServerMessage(QByteArray("Nothing special").toHex());
+    }
+
+    else if (cmd == "qfDllInfo") {
+        // That's the _first_ query package.
+        // Happens with  gdb 6.4.50.20060226-cvs / CodeSourcery.
+        // Never made it into FSF gdb that got qXfer:libraries:read instead.
+        // http://sourceware.org/ml/gdb/2007-05/msg00038.html
+        // Name=hexname,TextSeg=textaddr[,DataSeg=dataaddr]
+        sendGdbServerAck();
+        if (!m_session.libraries.isEmpty()) {
+            QByteArray response(1, 'm');
+            // FIXME: Limit packet length by using qsDllInfo packages?
+            for (int i = 0; i != m_session.libraries.size(); ++i) {
+                if (i)
+                    response += ';';
+                const trk::Library &lib = m_session.libraries.at(i);
+                response += "Name=" + lib.name.toHex()
+                            + ",TextSeg=" + trk::hexNumber(lib.codeseg)
+                            + ",DataSeg=" + trk::hexNumber(lib.dataseg);
+            }
+            sendGdbServerMessage(response, "library information transferred");
+        } else {
+            sendGdbServerMessage(QByteArray(1, 'l'), "library information transfer finished");
+        }
+    }
+
+    else if (cmd == "qsDllInfo") {
+        // That's a following query package
+        sendGdbServerAck();
+        sendGdbServerMessage(QByteArray(1, 'l'), "library information transfer finished");
+    }
+
+    else if (cmd == "qPacketInfo") {
+        // happens with  gdb 6.4.50.20060226-cvs / CodeSourcery
+        // deprecated by qSupported?
+        sendGdbServerAck();
+        sendGdbServerMessage("", "FIXME: nothing?");
+    }
+
+    else if (cmd == "qOffsets") {
+        sendGdbServerAck();
+        QByteArray answer = "TextSeg=";
+        answer.append(QByteArray::number(m_session.codeseg, 16));
+        answer.append(";DataSeg=");
+        answer.append(QByteArray::number(m_session.dataseg, 16));
+        sendGdbServerMessage(answer);
+    }
+
+    else if (cmd == "qSymbol::") {
+        if (m_verbose)
+            logMessage(msgGdbPacket(QLatin1String("notify can handle symbol lookup")));
+        // Notify the target that GDB is prepared to serve symbol lookup requests.
+        sendGdbServerAck();
+        if (1)
+            sendGdbServerMessage("OK", "no further symbols needed");
+        else
+            sendGdbServerMessage("qSymbol:" + QByteArray("_Z7E32Mainv").toHex(),
+                "ask for more");
+    }
+
+    else if (cmd.startsWith("qXfer:features:read:target.xml:")) {
+        //  $qXfer:features:read:target.xml:0,7ca#46...Ack
+        sendGdbServerAck();
+        sendGdbServerMessage("l<target><architecture>arm</architecture></target>");
+    }
+
+    else if (cmd == "qfThreadInfo") {
+        // That's the _first_ query package.
+        sendGdbServerAck();
+        if (!m_session.threads.isEmpty()) {
+            QByteArray response(1, 'm');
+            // FIXME: Limit packet length by using qsThreadInfo packages?
+            qDebug()  << "CURRENT THREAD: " << m_session.tid;
+            response += trk::hexNumber(m_session.tid);
+            sendGdbServerMessage(response, "thread information transferred");
+        } else {
+            sendGdbServerMessage(QByteArray(1, 'l'), "thread information transfer finished");
+        }
+    }
+
+    else if (cmd == "qsThreadInfo") {
+        // That's a following query package
+        sendGdbServerAck();
+        sendGdbServerMessage(QByteArray(1, 'l'), "thread information transfer finished");
+    }
+
+    else if (cmd.startsWith("qXfer:libraries:read")) {
+        //qDebug() << "COMMAND: " << cmd;
+        sendGdbServerAck();
+        QByteArray response = "l<library-list>";
+        for (int i = 0; i != m_session.libraries.size(); ++i) {
+            const trk::Library &lib = m_session.libraries.at(i);
+            response += "<library name=\"" + lib.name + "\">";
+            //response += "<segment address=\"0x" + hexNumber(lib.codeseg) + "\"/>";
+            response += "<section address=\"0x" + trk::hexNumber(lib.codeseg) + "\"/>";
+            response += "<section address=\"0x" + trk::hexNumber(lib.dataseg) + "\"/>";
+            response += "<section address=\"0x" + trk::hexNumber(lib.dataseg) + "\"/>";
+            response += "</library>";
+        }
+        response += "</library-list>";
+        sendGdbServerMessage(response, "library information transferred");
+    }
+
+    else if (cmd == "QStartNoAckMode") {
+        //$qSupported#37
+        logMessage("Handling 'QStartNoAckMode'");
+        sendGdbServerAck();
+        sendGdbServerMessage("OK", "ack no-ack mode");
+        m_gdbAckMode = false;
+    }
+
+    else if (cmd.startsWith("QPassSignals")) {
+        // list of signals to pass directly to inferior
+        // $QPassSignals:e;10;14;17;1a;1b;1c;21;24;25;4c;#8f
+        // happens only if "QPassSignals+;" is qSupported
+        sendGdbServerAck();
+        // FIXME: use the parameters
+        sendGdbServerMessage("OK", "passing signals accepted");
+    }
+
+    else if (cmd == "s" || cmd.startsWith("vCont;s")) {
+        logMessage(msgGdbPacket(QString::fromLatin1("Step range from 0x%1").
+                                arg(m_snapshot.registers[RegisterPC], 0, 16)));
+        sendGdbServerAck();
+        m_running = true;
+        sendTrkStepRange();
+    }
+
+    else if (cmd.startsWith('T')) {
+        // FIXME: check whether thread is alive
+        sendGdbServerAck();
+        sendGdbServerMessage("OK"); // pretend all is well
+    }
+
+    else if (cmd == "vCont?") {
+        // actions supported by the vCont packet
+        sendGdbServerAck();
+        sendGdbServerMessage("vCont;c;C;s;S"); // we don't support vCont.
+    }
+
+    else if (cmd == "vCont;c") {
+        // vCont[;action[:thread-id]]...'
+        sendGdbServerAck();
+        m_running = true;
+        sendTrkContinue();
+    }
+
+    else if (cmd.startsWith("Z0,") || cmd.startsWith("Z1,")) {
+        // Insert breakpoint
+        sendGdbServerAck();
+        logMessage(msgGdbPacket(QLatin1String("Insert breakpoint")));
+        // $Z0,786a4ccc,4#99
+        const int pos = cmd.lastIndexOf(',');
+        bool ok1 = false;
+        bool ok2 = false;
+        const uint addr = cmd.mid(3, pos - 3).toUInt(&ok1, 16);
+        const uint len = cmd.mid(pos + 1).toUInt(&ok2, 16);
+        if (!ok1) {
+            logMessage("MISPARSED ADDRESS FROM " + cmd +
+                " (" + cmd.mid(3, pos - 3) + ")" , LogError);
+        } else if (!ok2) {
+            logMessage("MISPARSED BREAKPOINT SIZE FROM " + cmd, LogError);
+        } else {
+            //qDebug() << "ADDR: " << hexNumber(addr) << " LEN: " << len;
+            logMessage(_("Inserting breakpoint at 0x%1, %2")
+                .arg(addr, 0, 16).arg(len));
+            // const QByteArray ba = trkBreakpointMessage(addr, len, len == 4);
+            tcftrk::Breakpoint bp(addr);
+            bp.size = len;
+            bp.setContextId(m_session.pid);
+            // We use the automatic ids calculated from the location
+            // address instead of the map in snapshot.
+            m_trkDevice->sendBreakpointsAddCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleAndReportSetBreakpoint),
+                                                   bp);
+        }
+    }
+
+    else if (cmd.startsWith("z0,") || cmd.startsWith("z1,")) {
+        // Remove breakpoint
+        sendGdbServerAck();
+        logMessage(msgGdbPacket(QLatin1String("Remove breakpoint")));
+        // $z0,786a4ccc,4#99
+        const int pos = cmd.lastIndexOf(',');
+        const uint addr = cmd.mid(3, pos - 3).toUInt(0, 16);
+        m_trkDevice->sendBreakpointsRemoveCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleClearBreakpoint),
+                                                  tcftrk::Breakpoint::idFromLocation(addr));
+    }
+
+    else if (cmd.startsWith("qPart:") || cmd.startsWith("qXfer:"))  {
+        QByteArray data = cmd.mid(1 + cmd.indexOf(':'));
+        // "qPart:auxv:read::0,147": Read OS auxiliary data (see info aux)
+        bool handled = false;
+        if (data.startsWith("auxv:read::")) {
+            const int offsetPos = data.lastIndexOf(':') + 1;
+            const int commaPos = data.lastIndexOf(',');
+            if (commaPos != -1) {
+                bool ok1 = false, ok2 = false;
+                const int offset = data.mid(offsetPos,  commaPos - offsetPos)
+                    .toUInt(&ok1, 16);
+                const int length = data.mid(commaPos + 1).toUInt(&ok2, 16);
+                if (ok1 && ok2) {
+                    const QString msg = _("Read of OS auxiliary "
+                        "vector (%1, %2) not implemented.").arg(offset).arg(length);
+                    logMessage(msgGdbPacket(msg), LogWarning);
+                    sendGdbServerMessage("E20", msg.toLatin1());
+                    handled = true;
+                }
+            }
+        } // auxv read
+
+        if (!handled) {
+            const QString msg = QLatin1String("FIXME unknown 'XFER'-request: ")
+                + QString::fromAscii(cmd);
+            logMessage(msgGdbPacket(msg), LogWarning);
+            sendGdbServerMessage("E20", msg.toLatin1());
+        }
+
+    } // qPart/qXfer
+       else {
+        logMessage(msgGdbPacket(QLatin1String("FIXME unknown: ")
+            + QString::fromAscii(cmd)), LogWarning);
+    }
+}
+
+void TcfTrkGdbAdapter::interruptInferior()
+{
+    m_trkDevice->sendRunControlSuspendCommand(TcfTrkCallback(), m_tcfProcessId);
+}
+
+void TcfTrkGdbAdapter::startAdapter()
+{
+    const ushort tcfTrkPort = 1534;
+
+    m_snapshot.fullReset();
+    m_firstModuleResumableEvent = true;
+    m_tcfProcessId.clear();
+
+    // Retrieve parameters
+    const DebuggerStartParameters &parameters = startParameters();
+    m_remoteExecutable = parameters.executable;
+    m_remoteArguments = parameters.processArgs;
+    m_symbolFile = parameters.symbolFileName;
+
+    QString tcfTrkAddress;
+    QSharedPointer<QTcpSocket> tcfTrkSocket(new QTcpSocket);
+    m_trkDevice->setDevice(tcfTrkSocket);
+    m_trkIODevice = tcfTrkSocket;
+
+    if (debug)
+        qDebug() << parameters.processArgs;
+    // Fixme: 1 of 3 testing hacks.
+    if (parameters.processArgs.size() >= 5 && parameters.processArgs.at(0) == _("@tcf@")) {
+        m_remoteExecutable = parameters.processArgs.at(1);
+        m_uid = parameters.processArgs.at(2).toUInt(0, 16);
+        m_symbolFile = parameters.processArgs.at(3);
+        tcfTrkAddress = parameters.processArgs.at(4);
+        m_remoteArguments.clear();
+    } else {
+        emit adapterStartFailed(_("Parameter error"), QString());
+    }
+
+    // Unixish gdbs accept only forward slashes
+    m_symbolFile.replace(QLatin1Char('\\'), QLatin1Char('/'));
+    // Start
+    QTC_ASSERT(state() == EngineStarting, qDebug() << state());
+    setState(AdapterStarting);
+    showMessage(_("TRYING TO START ADAPTER"));
+    logMessage(QLatin1String("### Starting TcfTrkGdbAdapter"));
+
+    QTC_ASSERT(m_gdbServer == 0, delete m_gdbServer);
+    QTC_ASSERT(m_gdbConnection == 0, m_gdbConnection = 0);
+    m_gdbServer = new QTcpServer(this);
+
+    if (!m_gdbServer->listen(QHostAddress(gdbServerIP()), gdbServerPort())) {
+        QString msg = QString("Unable to start the gdb server at %1: %2.")
+            .arg(m_gdbServerName).arg(m_gdbServer->errorString());
+        logMessage(msg, LogError);
+        emit adapterStartFailed(msg, QString());
+        return;
+    }
+
+    logMessage(QString("Gdb server running on %1.\nLittle endian assumed.")
+        .arg(m_gdbServerName));
+
+    connect(m_gdbServer, SIGNAL(newConnection()),
+        this, SLOT(handleGdbConnection()));
+
+    logMessage(QString::fromLatin1("Connecting to TCF TRK on %1:%2")
+               .arg(tcfTrkAddress).arg(tcfTrkPort));
+    tcfTrkSocket->connectToHost(tcfTrkAddress, tcfTrkPort);
+}
+
+void TcfTrkGdbAdapter::startInferior()
+{
+    QTC_ASSERT(state() == InferiorStarting, qDebug() << state());
+    m_trkDevice->sendProcessStartCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleCreateProcess),
+                                         m_remoteExecutable, m_uid, m_remoteArguments,
+                                         QString(), true);
+
+}
+
+void TcfTrkGdbAdapter::handleCreateProcess(const tcftrk::TcfTrkCommandResult &result)
+{
+    if (debug)
+        qDebug() << "ProcessCreated: " << result.toString();
+    if (!result) {
+        const QString errorMessage = result.errorString();
+        logMessage(QString::fromLatin1("Failed to start process: %1").arg(errorMessage), LogError);
+        emit inferiorStartFailed(result.errorString());
+        return;
+    }
+    QTC_ASSERT(!result.values.isEmpty(), return);
+
+    tcftrk::RunControlContext ctx;
+    ctx.parse(result.values.front());
+    logMessage(ctx.toString());
+
+    m_session.pid = ctx.osid.toUInt();
+    m_session.tid = 0; // Id is "p232.t34435"
+    const int sepPos = ctx.id.indexOf('t');
+    if (sepPos != -1)
+        m_session.tid = ctx.id.right(sepPos + 1).toUInt();
+    // See ModuleLoadSuspendedEvent for the rest.
+    m_session.codeseg = 0;
+    m_session.dataseg = 0;
+}
+
+void TcfTrkGdbAdapter::startInferiorPhase2()
+{
+    m_engine->continueInferiorInternal();
+}
+
+//
+// AbstractGdbAdapter interface implementation
+//
+
+void TcfTrkGdbAdapter::write(const QByteArray &data)
+{
+    // Write magic packets directly to TRK.
+    if (data.startsWith("@#")) {
+        QByteArray data1 = data.mid(2);
+        if (data1.endsWith(char(10)))
+            data1.chop(1);
+        if (data1.endsWith(char(13)))
+            data1.chop(1);
+        if (data1.endsWith(' '))
+            data1.chop(1);
+        bool ok;
+        const uint addr = data1.toUInt(&ok, 0);
+        logMessage(QString::fromLatin1("Direct step (@#) 0x%1: not implemented").arg(addr, 0, 16), LogError);
+        // directStep(addr);
+        return;
+    }
+    if (data.startsWith("@$")) {
+        QByteArray ba = QByteArray::fromHex(data.mid(2));
+        qDebug() << "Writing: " << quoteUnprintableLatin1(ba);
+        // if (ba.size() >= 1)
+        // sendTrkMessage(ba.at(0), TrkCB(handleDirectTrk), ba.mid(1));
+        return;
+    }
+    if (data.startsWith("@@")) {
+        logMessage(QLatin1String("Direct write (@@): not implemented"), LogError);
+        return;
+    }
+    m_gdbProc.write(data);
+}
+
+
+void TcfTrkGdbAdapter::cleanup()
+{
+    delete m_gdbServer;
+    m_gdbServer = 0;
+    if (!m_trkIODevice.isNull()) {
+        if (QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(m_trkIODevice.data())) {
+            if (socket->state() == QAbstractSocket::ConnectedState)
+                socket->disconnect();
+        } else {
+            m_trkIODevice->close();
+        }
+    }
+}
+
+void TcfTrkGdbAdapter::shutdown()
+{
+    cleanup();
+}
+
+void TcfTrkGdbAdapter::trkReloadRegisters()
+{
+    // Take advantage of direct access to cached register values.
+    QTC_ASSERT(m_snapshot.registerValid, /**/);
+    RegisterHandler *handler = m_engine->registerHandler();
+    Registers registers = handler->registers();
+
+    QTC_ASSERT(registers.size() >= 26,
+        qDebug() << "HAVE: " << registers.size(); return);
+    for (int i = 0; i < 16; ++i) {
+        Register &reg = registers[i];
+        QString value = trk::hexxNumber(m_snapshot.registers[i]);
+        reg.changed = (value != reg.value);
+        if (reg.changed)
+            reg.value = value;
+    }
+    Register &reg = registers[25];
+    QString value = trk::hexxNumber(m_snapshot.registers[16]);
+    reg.changed = (value != reg.value);
+    if (reg.changed)
+        reg.value = value;
+    handler->setRegisters(registers);
+}
+
+void TcfTrkGdbAdapter::trkReloadThreads()
+{
+    // Take advantage of direct access to cached register values.
+    QTC_ASSERT(m_snapshot.registerValid, /**/);
+    Threads threads;
+    foreach (const trk::Session::Thread &thread, m_session.threads) {
+        threads.append(thread);
+    }
+    ThreadsHandler *handler = m_engine->threadsHandler();
+    handler->setThreads(threads);
+}
+
+void TcfTrkGdbAdapter::handleWriteRegister(const tcftrk::TcfTrkCommandResult &result)
+{
+    const int registerNumber = result.cookie.toInt();
+    if (result) {
+        sendGdbServerMessage("OK");
+    } else {
+        logMessage(QString::fromLatin1("ERROR writing register #%1: %2").arg(registerNumber).arg(result.errorString()), LogError);
+        sendGdbServerMessage("E01");
+    }
+}
+
+void TcfTrkGdbAdapter::reportRegisters()
+{
+    QByteArray ba;
+    for (int i = 0; i < 16; ++i) {
+        const uint reg = trk::swapEndian(m_snapshot.registers[i]);
+        ba += trk::hexNumber(reg, 8);
+    }
+    QByteArray logMsg = "REGISTER CONTENTS: ";
+    if (m_verbose > 1) {
+        for (int i = 0; i < RegisterCount; ++i) {
+            logMsg += dumpRegister(i, m_snapshot.registers[i]);
+            logMsg += ' ';
+        }
+    }
+    sendGdbServerMessage(ba, logMsg);
+}
+
+void TcfTrkGdbAdapter::handleReadRegisters(const tcftrk::TcfTrkCommandResult &result)
+{
+    logMessage("       REGISTER RESULT: " + result.toString());
+    if (!result) {
+        logMessage("ERROR: " + result.errorString(), LogError);
+        return;
+    }
+    if (result.values.isEmpty() || result.values.front().type() != tcftrk::JsonValue::Array) {
+        logMessage(_("Format error in register message: ") + result.toString(), LogError);
+        return;
+    }
+    unsigned i = result.cookie.toUInt();
+    foreach (const tcftrk::JsonValue &jr, result.values.front().children())
+        m_snapshot.registers[i++] = jr.data().toUInt(0, 16);
+    m_snapshot.registerValid = true;
+    if (debug)
+        qDebug() << "handleReadRegisters: " << m_snapshot.toString();
+}
+
+void TcfTrkGdbAdapter::handleAndReportReadRegisters(const tcftrk::TcfTrkCommandResult &result)
+{
+    handleReadRegisters(result);
+    reportRegisters();
+}
+
+void TcfTrkGdbAdapter::handleAndReportReadRegister(const tcftrk::TcfTrkCommandResult &result)
+{
+    handleReadRegisters(result);
+    uint registerNumber = result.cookie.toUInt();
+    QByteArray logMsg = "Read Register";
+    if (registerNumber == RegisterPSGdb) {
+        QByteArray ba;
+        trk::appendInt(&ba, m_snapshot.registers[RegisterPSTrk], trk::LittleEndian);
+        logMsg += dumpRegister(registerNumber, m_snapshot.registers[RegisterPSTrk]);
+        sendGdbServerMessage(ba.toHex(), logMsg);
+    } else if (registerNumber < 16) {
+        QByteArray ba;
+        trk::appendInt(&ba, m_snapshot.registers[registerNumber], trk::LittleEndian);
+        logMsg += dumpRegister(registerNumber, m_snapshot.registers[registerNumber]);
+        sendGdbServerMessage(ba.toHex(), logMsg);
+    } else {
+        sendGdbServerMessage("0000", "read single unknown register #"
+            + QByteArray::number(registerNumber));
+        //sendGdbServerMessage("E01", "read single unknown register");
+    }
+}
+
+void TcfTrkGdbAdapter::handleAndReportReadRegistersAfterStop(const tcftrk::TcfTrkCommandResult &result)
+{
+    handleReadRegisters(result);
+    QByteArray ba = "T05";
+    for (int i = 0; i < 16; ++i)
+        appendRegister(&ba, i, m_snapshot.registers[i]);
+    // FIXME: those are not understood by gdb 6.4
+    //for (int i = 16; i < 25; ++i)
+    //    appendRegister(&ba, i, 0x0);
+    appendRegister(&ba, RegisterPSGdb, m_snapshot.registers[RegisterPSTrk]);
+    //qDebug() << "TrkGdbAdapter::handleAndReportReadRegistersAfterStop" << ba;
+    sendGdbServerMessage(ba, "Registers");
+}
+
+void TcfTrkGdbAdapter::handleAndReportSetBreakpoint(const tcftrk::TcfTrkCommandResult &result)
+{
+    if (result) {
+        sendGdbServerMessage("OK");
+    } else {
+        logMessage(QLatin1String("Error setting breakpoint: ") + result.errorString(), LogError);
+        sendGdbServerMessage("E21");
+    }
+}
+
+void TcfTrkGdbAdapter::handleClearBreakpoint(const tcftrk::TcfTrkCommandResult &result)
+{
+    logMessage("CLEAR BREAKPOINT ");
+    if (!result)
+        logMessage("Error clearing breakpoint: " + result.errorString(), LogError);
+    sendGdbServerMessage("OK");
+}
+
+void TcfTrkGdbAdapter::handleSignalContinue(const tcftrk::TcfTrkCommandResult &result)
+{
+    uint signalNumber = result.cookie.toUInt();
+    logMessage(QString::fromLatin1("   HANDLE SIGNAL CONTINUE %1").arg(signalNumber));
+    sendGdbServerMessage("O" + QByteArray("Console output").toHex());
+    sendGdbServerMessage("W81"); // "Process exited with result 1
+}
+
+void TcfTrkGdbAdapter::readMemory(uint addr, uint len, bool buffered)
+{
+    Q_ASSERT(len < (2 << 16));
+
+    // We try to get medium-sized chunks of data from the device
+    if (m_verbose > 2)
+        logMessage(_("readMemory %1 bytes from 0x%2 blocksize=%3")
+            .arg(len).arg(addr, 0, 16).arg(MemoryChunkSize));
+
+    m_snapshot.wantedMemory = MemoryRange(addr, addr + len);
+    tryAnswerGdbMemoryRequest(buffered);
+}
+
+static QString msgMemoryReadError(uint addr, uint len = 0)
+{
+    const QString lenS = len ? QString::number(len) : QLatin1String("<unknown>");
+    return _("Memory read error at: 0x%1 %2").arg(addr, 0, 16).arg(lenS);
+}
+
+// Format log message for memory access with some smartness about registers
+QByteArray TcfTrkGdbAdapter::memoryReadLogMessage(uint addr, const QByteArray &ba) const
+{
+    QByteArray logMsg = "memory contents";
+    if (m_verbose > 1) {
+        logMsg += " addr: " + trk::hexxNumber(addr);
+        // indicate dereferencing of registers
+        if (ba.size() == 4) {
+            if (addr == m_snapshot.registers[RegisterPC]) {
+                logMsg += "[PC]";
+            } else if (addr == m_snapshot.registers[RegisterPSTrk]) {
+                logMsg += "[PSTrk]";
+            } else if (addr == m_snapshot.registers[RegisterSP]) {
+                logMsg += "[SP]";
+            } else if (addr == m_snapshot.registers[RegisterLR]) {
+                logMsg += "[LR]";
+            } else if (addr > m_snapshot.registers[RegisterSP] &&
+                    (addr - m_snapshot.registers[RegisterSP]) < 10240) {
+                logMsg += "[SP+"; // Stack area ...stack seems to be top-down
+                logMsg += QByteArray::number(addr - m_snapshot.registers[RegisterSP]);
+                logMsg += ']';
+            }
+        }
+        logMsg += " length ";
+        logMsg += QByteArray::number(ba.size());
+        logMsg += " :";
+        logMsg += trk::stringFromArray(ba, 16).toAscii();
+    }
+    return logMsg;
+}
+
+void TcfTrkGdbAdapter::sendMemoryGetCommand(const MemoryRange &range, bool buffered)
+{
+    const QVariant cookie = QVariant::fromValue(range);
+    const TcfTrkCallback cb = buffered ?
+                              TcfTrkCallback(this, &TcfTrkGdbAdapter::handleReadMemoryBuffered) :
+                              TcfTrkCallback(this, &TcfTrkGdbAdapter::handleReadMemoryUnbuffered);
+    m_trkDevice->sendMemoryGetCommand(cb, m_tcfProcessId, range.from, range.size(), cookie);
+}
+
+void TcfTrkGdbAdapter::handleReadMemoryBuffered(const tcftrk::TcfTrkCommandResult &result)
+{
+    QTC_ASSERT(qVariantCanConvert<MemoryRange>(result.cookie), return);
+
+    const QByteArray memory = tcftrk::TcfTrkDevice::parseMemoryGet(result);
+    const MemoryRange range = result.cookie.value<MemoryRange>();
+
+    if (unsigned(memory.size()) != range.size()) {
+        logMessage(_("TEMPORARY: ") + msgMemoryReadError(range.from, range.size()));
+        logMessage(_("RETRYING UNBUFFERED"));
+        // FIXME: This does not handle large requests properly.
+        sendMemoryGetCommand(range, false);
+        return;
+    }
+    m_snapshot.insertMemory(range, memory);
+    tryAnswerGdbMemoryRequest(true);
+}
+
+void TcfTrkGdbAdapter::handleReadMemoryUnbuffered(const tcftrk::TcfTrkCommandResult &result)
+{
+    QTC_ASSERT(qVariantCanConvert<MemoryRange>(result.cookie), return);
+
+    const QByteArray memory = tcftrk::TcfTrkDevice::parseMemoryGet(result);
+    const MemoryRange range = result.cookie.value<MemoryRange>();
+
+    if (unsigned(memory.size()) != range.size()) {
+        logMessage(_("TEMPORARY: ") + msgMemoryReadError(range.from, range.size()));
+        logMessage(_("RETRYING UNBUFFERED"));
+        const QByteArray ba = "E20";
+        sendGdbServerMessage(ba, msgMemoryReadError(32, range.from).toLatin1());
+        return;
+    }
+    m_snapshot.insertMemory(range, memory);
+    tryAnswerGdbMemoryRequest(false);
+}
+
+void TcfTrkGdbAdapter::tryAnswerGdbMemoryRequest(bool buffered)
+{
+    //logMessage("TRYING TO ANSWER MEMORY REQUEST ");
+    MemoryRange wanted = m_snapshot.wantedMemory;
+    MemoryRange needed = m_snapshot.wantedMemory;
+    MEMORY_DEBUG("WANTED: " << wanted);
+    Snapshot::Memory::const_iterator it = m_snapshot.memory.begin();
+    Snapshot::Memory::const_iterator et = m_snapshot.memory.end();
+    for ( ; it != et; ++it) {
+        MEMORY_DEBUG("   NEEDED STEP: " << needed);
+        needed -= it.key();
+    }
+    MEMORY_DEBUG("NEEDED FINAL: " << needed);
+
+    if (needed.to == 0) {
+        // FIXME: need to combine chunks first.
+
+        // All fine. Send package to gdb.
+        it = m_snapshot.memory.begin();
+        et = m_snapshot.memory.end();
+        for ( ; it != et; ++it) {
+            if (it.key().from <= wanted.from && wanted.to <= it.key().to) {
+                int offset = wanted.from - it.key().from;
+                int len = wanted.to - wanted.from;
+                QByteArray ba = it.value().mid(offset, len);
+                sendGdbServerMessage(ba.toHex(), memoryReadLogMessage(wanted.from, ba));
+                return;
+            }
+        }
+        // Happens when chunks are not combined
+        QTC_ASSERT(false, /**/);
+        showMessage("CHUNKS NOT COMBINED");
+#        ifdef MEMORY_DEBUG
+        qDebug() << "CHUNKS NOT COMBINED";
+        it = m_snapshot.memory.begin();
+        et = m_snapshot.memory.end();
+        for ( ; it != et; ++it)
+            qDebug() << trk::hexNumber(it.key().from) << trk::hexNumber(it.key().to);
+        qDebug() << "WANTED" << wanted.from << wanted.to;
+#        endif
+        sendGdbServerMessage("E22", "");
+        return;
+    }
+
+    MEMORY_DEBUG("NEEDED AND UNSATISFIED: " << needed);
+    if (buffered) {
+        uint blockaddr = (needed.from / MemoryChunkSize) * MemoryChunkSize;
+        logMessage(_("Requesting buffered memory %1 bytes from 0x%2")
+            .arg(MemoryChunkSize).arg(blockaddr, 0, 16));
+        MemoryRange range(blockaddr, blockaddr + MemoryChunkSize);
+        MEMORY_DEBUG("   FETCH BUFFERED MEMORY : " << range);
+        sendMemoryGetCommand(range, true);
+    } else { // Unbuffered, direct requests
+        int len = needed.to - needed.from;
+        logMessage(_("Requesting unbuffered memory %1 bytes from 0x%2")
+            .arg(len).arg(needed.from, 0, 16));
+        sendMemoryGetCommand(needed, false);
+        MEMORY_DEBUG("   FETCH UNBUFFERED MEMORY : " << needed);
+    }
+}
+
+void TcfTrkGdbAdapter::handleWriteMemory(const tcftrk::TcfTrkCommandResult &result)
+{
+    if (result) {
+        sendGdbServerMessage("OK", "Write memory");
+    } else {
+        logMessage(QLatin1String("Error writing memory: ") + result.errorString(), LogError);
+        sendGdbServerMessage("E21");
+    }
+}
+
+QByteArray TcfTrkGdbAdapter::mainThreadContextId() const
+{
+    return tcftrk::RunControlContext::tcfId(m_session.pid, m_session.tid);
+}
+
+void TcfTrkGdbAdapter::sendTrkContinue()
+{
+    m_trkDevice->sendRunControlResumeCommand(TcfTrkCallback(), mainThreadContextId());
+}
+
+void TcfTrkGdbAdapter::sendTrkStepRange()
+{
+    uint from = m_snapshot.lineFromAddress;
+    uint to = m_snapshot.lineToAddress;
+    const uint pc = m_snapshot.registers[RegisterPC];
+    if (from <= pc && pc <= to) {
+        const QString msg = QString::fromLatin1("Step in 0x%1 .. 0x%2 instead of 0x%3...").
+                            arg(from, 0, 16).arg(to, 0, 16).arg(pc, 0, 16);
+        showMessage(msg);
+    } else {
+        from = pc;
+        to = pc;
+    }
+    // TODO: Step range does not seem to work yet?
+    const tcftrk::RunControlResumeMode mode = (from == to && to == pc) ?
+          (m_snapshot.stepOver ? tcftrk::RM_STEP_OVER       : tcftrk::RM_STEP_INTO) :
+          (m_snapshot.stepOver ? tcftrk::RM_STEP_OVER_RANGE : tcftrk::RM_STEP_INTO_RANGE);
+
+    logMessage(QString::fromLatin1("Stepping from 0x%1 to 0x%2 (current PC=0x%3), mode %4").
+               arg(from, 0, 16).arg(to, 0, 16).arg(pc).arg(int(mode)));
+    m_trkDevice->sendRunControlResumeCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleStep),
+                                             mainThreadContextId(),
+                                             mode, 1, from, to);
+}
+
+void TcfTrkGdbAdapter::handleStep(const tcftrk::TcfTrkCommandResult &result)
+{
+
+    if (!result) { // Try fallback with Continue.
+        logMessage(QString::fromLatin1("Error while stepping: %1 (fallback to 'continue')").arg(result.errorString()), LogWarning);
+        sendTrkContinue();
+        // Doing nothing as below does not work as gdb seems to insist on
+        // making some progress through a 'step'.
+        //sendTrkMessage(0x12,
+        //    TrkCB(handleAndReportReadRegistersAfterStop),
+        //    trkReadRegistersMessage());
+        return;
+    }
+    // The gdb server response is triggered later by the Stop Reply packet.
+    logMessage("STEP FINISHED " + currentTime());
+}
+
+
+} // namespace Internal
+} // namespace Debugger
diff --git a/src/plugins/debugger/gdb/tcftrkgdbadapter.h b/src/plugins/debugger/gdb/tcftrkgdbadapter.h
new file mode 100644
index 0000000000000000000000000000000000000000..0953700cdb8b2c8a02f42a4c5a37fe47d01d6e89
--- /dev/null
+++ b/src/plugins/debugger/gdb/tcftrkgdbadapter.h
@@ -0,0 +1,186 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 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_TCFTRKGDBADAPTER_H
+#define DEBUGGER_TCFTRKGDBADAPTER_H
+
+#include "abstractgdbadapter.h"
+
+#include "callback.h"
+#include "trkutils.h"
+#include "symbian.h"
+
+#include <QtCore/QPointer>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QStringList>
+#include <QtCore/QHash>
+
+QT_BEGIN_NAMESPACE
+class QTcpServer;
+class QTcpSocket;
+class QIODevice;
+QT_END_NAMESPACE
+
+namespace tcftrk {
+    struct TcfTrkCommandResult;
+    class TcfTrkDevice;
+    class TcfTrkEvent;
+    class TcfTrkRunControlModuleLoadContextSuspendedEvent;
+}
+
+namespace Debugger {
+namespace Internal {
+
+struct MemoryRange;
+struct GdbResult;
+
+///////////////////////////////////////////////////////////////////////
+//
+// TcfTrkGdbAdapter
+//
+///////////////////////////////////////////////////////////////////////
+
+class TcfTrkGdbAdapter : public AbstractGdbAdapter
+{
+    Q_OBJECT
+
+public:
+    typedef trk::Callback<const GdbResult &> GdbResultCallback;
+    typedef trk::Callback<const tcftrk::TcfTrkCommandResult &> TcfTrkCallback;
+    typedef trk::Callback<const GdbResponse &> GdbCallback;
+
+    explicit TcfTrkGdbAdapter(GdbEngine *engine);
+    virtual ~TcfTrkGdbAdapter();
+    void setGdbServerName(const QString &name);
+    QString gdbServerName() const { return m_gdbServerName; }
+    QString gdbServerIP() const;
+    uint gdbServerPort() const;
+    Q_SLOT void setVerbose(const QVariant &value);
+    void setVerbose(int verbose);
+    void setBufferedMemoryRead(bool b) { m_bufferedMemoryRead = b; }
+
+    void trkReloadRegisters();
+    void trkReloadThreads();
+
+signals:
+    void output(const QString &msg);
+
+public:
+    //
+    // Implementation of GdbProcessBase
+    //
+    void start(const QString &program, const QStringList &args,
+        QIODevice::OpenMode mode = QIODevice::ReadWrite);
+    void write(const QByteArray &data);
+    bool isTrkAdapter() const { return true; }
+
+    virtual DumperHandling dumperHandling() const { return DumperNotAvailable; }
+
+private:
+    void startAdapter();
+    void startInferior();
+    void startInferiorPhase2();
+    void interruptInferior();
+    void shutdown();
+    void handleWriteRegister(const tcftrk::TcfTrkCommandResult &result);
+    void reportRegisters();
+    void handleReadRegisters(const tcftrk::TcfTrkCommandResult &result);
+    void handleAndReportReadRegisters(const tcftrk::TcfTrkCommandResult &result);
+    void handleAndReportReadRegister(const tcftrk::TcfTrkCommandResult &result);
+    void handleAndReportReadRegistersAfterStop(const tcftrk::TcfTrkCommandResult &result);
+    void handleAndReportSetBreakpoint(const tcftrk::TcfTrkCommandResult &result);
+    void handleClearBreakpoint(const tcftrk::TcfTrkCommandResult &result);
+    void handleSignalContinue(const tcftrk::TcfTrkCommandResult &result);
+    void readMemory(uint addr, uint len, bool buffered);
+    void handleReadMemoryBuffered(const tcftrk::TcfTrkCommandResult &result);
+    void handleReadMemoryUnbuffered(const tcftrk::TcfTrkCommandResult &result);
+    void handleWriteMemory(const tcftrk::TcfTrkCommandResult &result);
+    void tryAnswerGdbMemoryRequest(bool buffered);
+    inline void sendMemoryGetCommand(const MemoryRange &range, bool buffered);
+    inline QByteArray mainThreadContextId() const;
+    QByteArray memoryReadLogMessage(uint addr, const QByteArray &ba) const;
+
+    AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
+
+    void cleanup();
+
+    void handleTargetRemote(const GdbResponse &response);
+
+    QString m_gdbServerName; // 127.0.0.1:(2222+uid)
+    bool m_running;
+    tcftrk::TcfTrkDevice *m_trkDevice;
+    QSharedPointer<QIODevice> m_trkIODevice;
+
+    //
+    // Gdb
+    //
+    Q_SLOT void handleGdbConnection();
+    Q_SLOT void readGdbServerCommand();
+    Q_SLOT void tcftrkDeviceError(const QString  &);
+    void startGdb();
+    Q_SLOT void tcftrkEvent(const tcftrk::TcfTrkEvent &knownEvent);
+    void handleTcfTrkRunControlModuleLoadContextSuspendedEvent(const tcftrk::TcfTrkRunControlModuleLoadContextSuspendedEvent &e);
+    inline void sendTrkContinue();
+    void sendTrkStepRange();
+    void handleStep(const tcftrk::TcfTrkCommandResult &result);
+    void handleCreateProcess(const tcftrk::TcfTrkCommandResult &result);
+
+    void readGdbResponse();
+    void handleGdbServerCommand(const QByteArray &cmd);
+    void sendGdbServerMessage(const QByteArray &msg,
+        const QByteArray &logNote = QByteArray());
+    void sendGdbServerAck();
+    bool sendGdbServerPacket(const QByteArray &packet, bool doFlush);
+
+    void logMessage(const QString &msg, int channel = LogDebug);  // triggers output() if m_verbose
+    Q_SLOT void trkLogMessage(const QString &msg);
+
+    QPointer<QTcpServer> m_gdbServer;
+    QPointer<QTcpSocket> m_gdbConnection;
+    QByteArray m_gdbReadBuffer;
+    bool m_gdbAckMode;
+
+    // Debuggee state
+    trk::Session m_session; // global-ish data (process id, target information)
+    Symbian::Snapshot m_snapshot; // local-ish data (memory and registers)
+    QString m_remoteExecutable;
+    unsigned m_uid;
+    QStringList m_remoteArguments;
+    QString m_symbolFile;
+    int m_verbose;
+    bool m_bufferedMemoryRead;
+    bool m_firstModuleResumableEvent;
+    QByteArray m_tcfProcessId;
+    LocalGdbProcess m_gdbProc;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_TCFTRKGDBADAPTER_H
diff --git a/src/plugins/debugger/gdb/trkgdbadapter.cpp b/src/plugins/debugger/gdb/trkgdbadapter.cpp
index b417d688a08fd3b0b57f1bfaa225a7e3d40dd79b..98f36a8be61ccc0bf84e5e7a32d92b8f3a2fe735 100644
--- a/src/plugins/debugger/gdb/trkgdbadapter.cpp
+++ b/src/plugins/debugger/gdb/trkgdbadapter.cpp
@@ -48,6 +48,8 @@
 
 #include <QtCore/QTimer>
 #include <QtCore/QDir>
+#include <QtNetwork/QTcpServer>
+#include <QtNetwork/QTcpSocket>
 
 #ifdef Q_OS_WIN
 #  include <windows.h>
@@ -62,49 +64,14 @@
 
 #define TrkCB(s) TrkCallback(this, &TrkGdbAdapter::s)
 
-//#define DEBUG_MEMORY  1
-#if DEBUG_MEMORY
-#   define MEMORY_DEBUG(s) qDebug() << s
-#else
-#   define MEMORY_DEBUG(s)
-#endif
-#define MEMORY_DEBUGX(s) qDebug() << s
-
 using namespace trk;
 
 namespace Debugger {
 namespace Internal {
-
-enum { KnownRegisters = RegisterPSGdb + 1};
+using namespace Symbian;
 
 static inline void appendByte(QByteArray *ba, trk::byte b) { ba->append(b); }
 
-static const char *registerNames[KnownRegisters] =
-{
-    "A1", "A2", "A3", "A4",
-    0, 0, 0, 0,
-    0, 0, 0, "AP",
-    "IP", "SP", "LR", "PC",
-    "PSTrk", 0, 0, 0,
-    0, 0, 0, 0,
-    0, "PSGdb"
-};
-
-static QByteArray dumpRegister(uint n, uint value)
-{
-    QByteArray ba;
-    ba += ' ';
-    if (n < KnownRegisters && registerNames[n]) {
-        ba += registerNames[n];
-    } else {
-        ba += '#';
-        ba += QByteArray::number(n);
-    }
-    ba += '=';
-    ba += hexxNumber(value);
-    return ba;
-}
-
 static void appendRegister(QByteArray *ba, uint regno, uint value)
 {
     ba->append(hexNumber(regno, 2));
@@ -113,123 +80,6 @@ static void appendRegister(QByteArray *ba, uint regno, uint value)
     ba->append(';');
 }
 
-QDebug operator<<(QDebug d, MemoryRange range)
-{
-    return d << QString("[%1,%2] (size %3) ")
-        .arg(range.from, 0, 16).arg(range.to, 0, 16).arg(range.size());
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-// MemoryRange
-//
-///////////////////////////////////////////////////////////////////////////
-
-MemoryRange::MemoryRange(uint f, uint t)
-    : from(f), to(t)
-{
-    QTC_ASSERT(f <= t, qDebug() << "F: " << f << " T: " << t);
-}
-
-bool MemoryRange::intersects(const MemoryRange &other) const
-{
-    Q_UNUSED(other);
-    QTC_ASSERT(false, /**/);
-    return false; // FIXME
-}
-
-void MemoryRange::operator-=(const MemoryRange &other)
-{
-    if (from == 0 && to == 0)
-        return;
-    MEMORY_DEBUG("      SUB: "  << *this << " - " << other);
-    if (other.from <= from && to <= other.to) {
-        from = to = 0;
-        return;
-    }
-    if (other.from <= from && other.to <= to) {
-        from = qMax(from, other.to);
-        return;
-    }
-    if (from <= other.from && to <= other.to) {
-        to = qMin(other.from, to);
-        return;
-    }
-    // This would split the range.
-    QTC_ASSERT(false, qDebug() << "Memory::operator-() not handled for: "
-        << *this << " - " << other);
-}
-
-bool MemoryRange::isReadOnly() const
-{
-    return from >= 0x70000000 && to < 0x80000000;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-// Snapshot
-//
-///////////////////////////////////////////////////////////////////////////
-
-void Snapshot::reset()
-{
-    for (Memory::Iterator it = memory.begin(); it != memory.end(); ++it) {
-        if (it.key().isReadOnly()) {
-            MEMORY_DEBUG("KEEPING READ-ONLY RANGE" << it.key());
-        } else {
-            it = memory.erase(it);
-        }
-    }
-    for (int i = 0; i < RegisterCount; ++i)
-        registers[i] = 0;
-    registerValid = false;
-    wantedMemory = MemoryRange();
-    lineFromAddress = 0;
-    lineToAddress = 0;
-}
-
-void Snapshot::fullReset()
-{
-    memory.clear();
-    reset();
-}
-
-void Snapshot::insertMemory(const MemoryRange &range, const QByteArray &ba)
-{
-    QTC_ASSERT(range.size() == uint(ba.size()),
-        qDebug() << "RANGE: " << range << " BA SIZE: " << ba.size(); return);
-
-    MEMORY_DEBUG("INSERT: " << range);
-    // Try to combine with existing chunk.
-    Snapshot::Memory::iterator it = memory.begin();
-    Snapshot::Memory::iterator et = memory.end();
-    for ( ; it != et; ++it) {
-        if (range.from == it.key().to) {
-            MEMORY_DEBUG("COMBINING " << it.key() << " AND " << range);
-            QByteArray data = *it;
-            data.append(ba);
-            const MemoryRange res(it.key().from, range.to);
-            memory.remove(it.key());
-            MEMORY_DEBUG(" TO(1)  " << res);
-            insertMemory(res, data);
-            return;
-        }
-        if (it.key().from == range.to) {
-            MEMORY_DEBUG("COMBINING " << range << " AND " << it.key());
-            QByteArray data = ba;
-            data.append(*it);
-            const MemoryRange res(range.from, it.key().to);
-            memory.remove(it.key());
-            MEMORY_DEBUG(" TO(2)  " << res);
-            insertMemory(res, data);
-            return;
-        }
-    }
-
-    // Not combinable, add chunk.
-    memory.insert(range, ba);
-}
-
 ///////////////////////////////////////////////////////////////////////////
 //
 // TrkGdbAdapter
@@ -385,8 +235,8 @@ QByteArray TrkGdbAdapter::trkStepRangeMessage()
         from = pc;
         to = pc;
     }
-
-    //qDebug() << "USING" << int(option) << (option == 1 ? " INTO " : " OVER");
+    logMessage(QString::fromLatin1("Stepping from 0x%1 to 0x%2 (current PC=0x%3), option 0x%4").
+               arg(from, 0, 16).arg(to, 0, 16).arg(pc).arg(option, 0, 16));
     QByteArray ba;
     ba.reserve(17);
     appendByte(&ba, option);
@@ -431,10 +281,10 @@ void TrkGdbAdapter::slotEmitDelayedInferiorStartFailed()
 }
 
 
-void TrkGdbAdapter::logMessage(const QString &msg)
+void TrkGdbAdapter::logMessage(const QString &msg, int logChannel)
 {
-    if (m_verbose)
-        showMessage("TRK LOG: " + msg);
+    if (m_verbose || logChannel != LogDebug)
+        showMessage("TRK LOG: " + msg, logChannel);
 }
 
 //
@@ -478,7 +328,7 @@ void TrkGdbAdapter::readGdbServerCommand()
         }
 
         if (code == '-') {
-            logMessage("NAK: Retransmission requested");
+            logMessage("NAK: Retransmission requested", LogError);
             // This seems too harsh.
             //emit adapterCrashed("Communication problem encountered.");
             continue;
@@ -492,14 +342,14 @@ void TrkGdbAdapter::readGdbServerCommand()
 
         if (code != '$') {
             logMessage("Broken package (2) " + quoteUnprintableLatin1(ba)
-                + hexNumber(code));
+                + hexNumber(code), LogError);
             continue;
         }
 
         int pos = ba.indexOf('#');
         if (pos == -1) {
             logMessage("Invalid checksum format in "
-                + quoteUnprintableLatin1(ba));
+                + quoteUnprintableLatin1(ba), LogError);
             continue;
         }
 
@@ -507,7 +357,7 @@ void TrkGdbAdapter::readGdbServerCommand()
         uint checkSum = ba.mid(pos + 1, 2).toUInt(&ok, 16);
         if (!ok) {
             logMessage("Invalid checksum format 2 in "
-                + quoteUnprintableLatin1(ba));
+                + quoteUnprintableLatin1(ba), LogError);
             return;
         }
 
@@ -518,7 +368,7 @@ void TrkGdbAdapter::readGdbServerCommand()
 
         if (sum != checkSum) {
             logMessage(QString("ERROR: Packet checksum wrong: %1 %2 in "
-                + quoteUnprintableLatin1(ba)).arg(checkSum).arg(sum));
+                + quoteUnprintableLatin1(ba)).arg(checkSum).arg(sum), LogError);
         }
 
         QByteArray cmd = ba.left(pos);
@@ -531,17 +381,17 @@ bool TrkGdbAdapter::sendGdbServerPacket(const QByteArray &packet, bool doFlush)
 {
     if (!m_gdbConnection) {
         logMessage(_("Cannot write to gdb: No connection (%1)")
-            .arg(_(packet)));
+            .arg(_(packet)), LogError);
         return false;
     }
     if (m_gdbConnection->state() != QAbstractSocket::ConnectedState) {
         logMessage(_("Cannot write to gdb: Not connected (%1)")
-            .arg(_(packet)));
+            .arg(_(packet)), LogError);
         return false;
     }
     if (m_gdbConnection->write(packet) == -1) {
         logMessage(_("Cannot write to gdb: %1 (%2)")
-            .arg(m_gdbConnection->errorString()).arg(_(packet)));
+            .arg(m_gdbConnection->errorString()).arg(_(packet)), LogError);
         return false;
     }
     if (doFlush)
@@ -614,6 +464,18 @@ QByteArray TrkGdbAdapter::trkBreakpointMessage(uint addr, uint len, bool armMode
     return ba;
 }
 
+static QByteArray msgStepRangeReceived(unsigned from, unsigned to, bool over)
+{
+    QByteArray rc = "Stepping range received for step ";
+    rc += over ? "over" : "into";
+    rc += " (0x";
+    rc += QByteArray::number(from, 16);
+    rc += " to 0x";
+    rc += QByteArray::number(to, 16);
+    rc += ')';
+    return rc;
+}
+
 void TrkGdbAdapter::handleGdbServerCommand(const QByteArray &cmd)
 {
     // http://sourceware.org/gdb/current/onlinedocs/gdb_34.html
@@ -701,7 +563,7 @@ void TrkGdbAdapter::handleGdbServerCommand(const QByteArray &cmd)
         m_snapshot.lineFromAddress = cmd.mid(8, pos - 8).toUInt(0, 16);
         m_snapshot.lineToAddress = cmd.mid(pos + 1).toUInt(0, 16);
         m_snapshot.stepOver = false;
-        sendGdbServerMessage("", "Stepping range received for Step Into");
+        sendGdbServerMessage("", msgStepRangeReceived(m_snapshot.lineFromAddress, m_snapshot.lineToAddress, m_snapshot.stepOver));
     }
 
     else if (cmd.startsWith("salnext,")) {
@@ -711,7 +573,7 @@ void TrkGdbAdapter::handleGdbServerCommand(const QByteArray &cmd)
         m_snapshot.lineFromAddress = cmd.mid(8, pos - 8).toUInt(0, 16);
         m_snapshot.lineToAddress = cmd.mid(pos + 1).toUInt(0, 16);
         m_snapshot.stepOver = true;
-        sendGdbServerMessage("", "Stepping range received for Step Over");
+        sendGdbServerMessage("", msgStepRangeReceived(m_snapshot.lineFromAddress, m_snapshot.lineToAddress, m_snapshot.stepOver));
     }
 
     else if (cmd.startsWith("Hc")) {
@@ -969,8 +831,8 @@ void TrkGdbAdapter::handleGdbServerCommand(const QByteArray &cmd)
     }
 
     else if (cmd == "s" || cmd.startsWith("vCont;s")) {
-        logMessage(msgGdbPacket(QLatin1String("Step range")));
-        logMessage("  from " + hexxNumber(m_snapshot.registers[RegisterPC]));
+        logMessage(msgGdbPacket(QString::fromLatin1("Step range from 0x%1").
+                                arg(m_snapshot.registers[RegisterPC], 0, 16)));
         sendGdbServerAck();
         //m_snapshot.reset();
         m_running = true;
@@ -1012,9 +874,9 @@ void TrkGdbAdapter::handleGdbServerCommand(const QByteArray &cmd)
         const uint len = cmd.mid(pos + 1).toUInt(&ok2, 16);
         if (!ok1) {
             logMessage("MISPARSED ADDRESS FROM " + cmd +
-                " (" + cmd.mid(3, pos - 3) + ")");
+                " (" + cmd.mid(3, pos - 3) + ")", LogError);
         } else if (!ok2) {
-            logMessage("MISPARSED BREAKPOINT SIZE FROM " + cmd);
+            logMessage("MISPARSED BREAKPOINT SIZE FROM " + cmd, LogError);
         } else {
             //qDebug() << "ADDR: " << hexNumber(addr) << " LEN: " << len;
             logMessage(_("Inserting breakpoint at 0x%1, %2")
@@ -1036,7 +898,7 @@ void TrkGdbAdapter::handleGdbServerCommand(const QByteArray &cmd)
         const uint bp = m_session.addressToBP[addr];
         if (bp == 0) {
             logMessage(_("NO RECORDED BP AT 0x%1, %2")
-                .arg(addr, 0, 16).arg(len));
+                .arg(addr, 0, 16).arg(len), LogError);
             sendGdbServerMessage("E00");
         } else {
             m_session.addressToBP.remove(addr);
@@ -1061,7 +923,7 @@ void TrkGdbAdapter::handleGdbServerCommand(const QByteArray &cmd)
                 if (ok1 && ok2) {
                     const QString msg = _("Read of OS auxiliary "
                         "vector (%1, %2) not implemented.").arg(offset).arg(length);
-                    logMessage(msgGdbPacket(msg));
+                    logMessage(msgGdbPacket(msg), LogWarning);
                     sendGdbServerMessage("E20", msg.toLatin1());
                     handled = true;
                 }
@@ -1071,13 +933,13 @@ void TrkGdbAdapter::handleGdbServerCommand(const QByteArray &cmd)
         if (!handled) {
             const QString msg = QLatin1String("FIXME unknown 'XFER'-request: ")
                 + QString::fromAscii(cmd);
-            logMessage(msgGdbPacket(msg));
+            logMessage(msgGdbPacket(msg), LogWarning);
             sendGdbServerMessage("E20", msg.toLatin1());
         }
     } // qPart/qXfer
     else {
         logMessage(msgGdbPacket(QLatin1String("FIXME unknown: ")
-            + QString::fromAscii(cmd)));
+            + QString::fromAscii(cmd)), LogWarning);
     }
 }
 
@@ -1098,7 +960,7 @@ void TrkGdbAdapter::sendTrkAck(trk::byte token)
 
 void TrkGdbAdapter::handleTrkError(const QString &msg)
 {
-    logMessage("## TRK ERROR: " + msg);
+    logMessage("## TRK ERROR: " + msg, LogError);
     emit adapterCrashed("TRK problem encountered:\n" + msg);
 }
 
@@ -1110,8 +972,7 @@ void TrkGdbAdapter::handleTrkResult(const TrkResult &result)
         // It looks like those messages _must not_ be acknowledged.
         // If we do so, TRK will complain about wrong sequencing.
         //sendTrkAck(result.token);
-        logMessage(QLatin1String("APPLICATION OUTPUT: ") +
-            QString::fromAscii(result.data));
+        logMessage(QString::fromAscii(result.data), AppOutput);
         sendGdbServerMessage("O" + result.data.toHex());
         return;
     }
@@ -1125,7 +986,7 @@ void TrkGdbAdapter::handleTrkResult(const TrkResult &result)
             QString logMsg;
             QTextStream(&logMsg) << prefix << "NAK: for token=" << result.token
                 << " ERROR: " << errorMessage(result.data.at(0)) << ' ' << str;
-            logMessage(logMsg);
+            logMessage(logMsg, LogError);
             break;
         }
         case TrkNotifyStopped: {  // 0x90 Notified Stopped
@@ -1176,14 +1037,14 @@ void TrkGdbAdapter::handleTrkResult(const TrkResult &result)
         case TrkNotifyException: { // 0x91 Notify Exception (obsolete)
             showMessage(_("RESET SNAPSHOT (NOTIFY EXCEPTION)"));
             m_snapshot.reset();
-            logMessage(prefix + "NOTE: EXCEPTION  " + str);
+            logMessage(prefix + "NOTE: EXCEPTION  " + str, AppError);
             sendTrkAck(result.token);
             break;
         }
         case 0x92: { //
             showMessage(_("RESET SNAPSHOT (NOTIFY INTERNAL ERROR)"));
             m_snapshot.reset();
-            logMessage(prefix + "NOTE: INTERNAL ERROR: " + str);
+            logMessage(prefix + "NOTE: INTERNAL ERROR: " + str, LogError);
             sendTrkAck(result.token);
             break;
         }
@@ -1279,7 +1140,7 @@ void TrkGdbAdapter::handleTrkResult(const TrkResult &result)
             break;
         }
         default: {
-            logMessage(prefix + "INVALID: " + str);
+            logMessage(prefix + "INVALID: " + str, LogError);
             break;
         }
     }
@@ -1329,7 +1190,7 @@ void TrkGdbAdapter::handleReadRegisters(const TrkResult &result)
     // [80 0B 00   00 00 00 00   C9 24 FF BC   00 00 00 00   00
     //  60 00 00   00 00 00 00   78 67 79 70   00 00 00 00   00...]
     if (result.errorCode()) {
-        logMessage("ERROR: " + result.errorString());
+        logMessage("ERROR: " + result.errorString(), LogError);
         return;
     }
     const char *data = result.data.data() + 1; // Skip ok byte
@@ -1342,7 +1203,7 @@ void TrkGdbAdapter::handleWriteRegister(const TrkResult &result)
 {
     logMessage("       RESULT: " + result.toString() + result.cookie.toString());
     if (result.errorCode()) {
-        logMessage("ERROR: " + result.errorString());
+        logMessage("ERROR: " + result.errorString(), LogError);
         sendGdbServerMessage("E01");
         return;
     }
@@ -1449,7 +1310,7 @@ QByteArray TrkGdbAdapter::memoryReadLogMessage(uint addr, const QByteArray &ba)
 void TrkGdbAdapter::handleReadMemoryBuffered(const TrkResult &result)
 {
     if (extractShort(result.data.data() + 1) + 3 != result.data.size())
-        logMessage("\n BAD MEMORY RESULT: " + result.data.toHex() + "\n");
+        logMessage("\n BAD MEMORY RESULT: " + result.data.toHex() + "\n", LogError);
     const MemoryRange range = result.cookie.value<MemoryRange>();
     if (const int errorCode = result.errorCode()) {
         logMessage(_("TEMPORARY: ") + msgMemoryReadError(errorCode, range.from));
@@ -1467,7 +1328,7 @@ void TrkGdbAdapter::handleReadMemoryBuffered(const TrkResult &result)
 void TrkGdbAdapter::handleReadMemoryUnbuffered(const TrkResult &result)
 {
     if (extractShort(result.data.data() + 1) + 3 != result.data.size())
-        logMessage("\n BAD MEMORY RESULT: " + result.data.toHex() + "\n");
+        logMessage("\n BAD MEMORY RESULT: " + result.data.toHex() + "\n", LogError);
     const MemoryRange range = result.cookie.value<MemoryRange>();
     if (const int errorCode = result.errorCode()) {
         logMessage(_("TEMPORARY: ") + msgMemoryReadError(errorCode, range.from));
@@ -1587,7 +1448,7 @@ void TrkGdbAdapter::reportReadMemoryBuffered(const TrkResult &result)
 void TrkGdbAdapter::handleStep(const TrkResult &result)
 {
     if (result.errorCode()) {
-        logMessage("ERROR: " + result.errorString() + " in handleStep");
+        logMessage("ERROR: " + result.errorString() + " in handleStep", LogError);
 
         // Try fallback with Continue.
         showMessage("FALLBACK TO 'CONTINUE'");
@@ -1612,7 +1473,7 @@ void TrkGdbAdapter::handleAndReportSetBreakpoint(const TrkResult &result)
     //    Error: 0x00
     // [80 09 00 00 00 00 0A]
     if (result.errorCode()) {
-        logMessage("ERROR WHEN SETTING BREAKPOINT: " + result.errorString());
+        logMessage("ERROR WHEN SETTING BREAKPOINT: " + result.errorString(), LogError);
         sendGdbServerMessage("E21");
         return;
     }
@@ -1629,7 +1490,7 @@ void TrkGdbAdapter::handleClearBreakpoint(const TrkResult &result)
 {
     logMessage("CLEAR BREAKPOINT ");
     if (result.errorCode()) {
-        logMessage("ERROR: " + result.errorString());
+        logMessage("ERROR: " + result.errorString(), LogError);
         //return;
     }
     sendGdbServerMessage("OK");
@@ -1785,7 +1646,7 @@ void TrkGdbAdapter::startAdapter()
         if (message.isEmpty()) {
             emit adapterStartFailed(QString(), QString());
         } else {
-            logMessage(message);
+            logMessage(message, LogError);
             emit adapterStartFailed(message, QString());
         }
         return;
@@ -1798,7 +1659,7 @@ void TrkGdbAdapter::startAdapter()
     if (!m_gdbServer->listen(QHostAddress(gdbServerIP()), gdbServerPort())) {
         QString msg = QString("Unable to start the gdb server at %1: %2.")
             .arg(m_gdbServerName).arg(m_gdbServer->errorString());
-        logMessage(msg);
+        logMessage(msg, LogError);
         emit adapterStartFailed(msg, QString());
         return;
     }
@@ -1831,7 +1692,7 @@ void TrkGdbAdapter::handleCreateProcess(const TrkResult &result)
     //logMessage("       RESULT: " + result.toString());
     // [80 08 00   00 00 01 B5   00 00 01 B6   78 67 40 00   00 40 00 00]
     if (result.errorCode()) {
-        logMessage("ERROR: " + result.errorString());
+        logMessage("ERROR: " + result.errorString(), LogError);
         QString msg = _("Cannot start executable \"%1\" on the device:\n%2")
             .arg(m_remoteExecutable).arg(result.errorString());
         // Delay cleanup as not to close a trk device from its read handler,
@@ -1854,7 +1715,7 @@ void TrkGdbAdapter::handleCreateProcess(const TrkResult &result)
 
     const QByteArray symbolFile = m_symbolFile.toLocal8Bit();
     if (symbolFile.isEmpty()) {
-        logMessage(_("WARNING: No symbol file available."));
+        logMessage(_("WARNING: No symbol file available."), LogWarning);
     } else {
         // Does not seem to be necessary anymore.
         // FIXME: Startup sequence can be streamlined now as we do not
@@ -1942,7 +1803,7 @@ void TrkGdbAdapter::handleDirectWrite1(const TrkResult &response)
     scratch = m_session.dataseg + 512;
     logMessage("DIRECT WRITE1: " + response.toString());
     if (const int errorCode = response.errorCode()) {
-        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1");
+        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1", LogError);
     } else {
         oldMem = response.data.mid(3);
         oldPC = m_snapshot.registers[RegisterPC];
@@ -1987,7 +1848,7 @@ void TrkGdbAdapter::handleDirectWrite2(const TrkResult &response)
 {
     logMessage("DIRECT WRITE2: " + response.toString());
     if (const int errorCode = response.errorCode()) {
-        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1");
+        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1", LogError);
     } else {
         // Check
         sendTrkMessage(0x10, TrkCB(handleDirectWrite3),
@@ -1999,7 +1860,7 @@ void TrkGdbAdapter::handleDirectWrite3(const TrkResult &response)
 {
     logMessage("DIRECT WRITE3: " + response.toString());
     if (const int errorCode = response.errorCode()) {
-        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1");
+        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1", LogError);
     } else {
         // Set PC
         sendTrkMessage(0x13, TrkCB(handleDirectWrite4),
@@ -2013,7 +1874,7 @@ void TrkGdbAdapter::handleDirectWrite4(const TrkResult &response)
 return;
     logMessage("DIRECT WRITE4: " + response.toString());
     if (const int errorCode = response.errorCode()) {
-        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1");
+        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1", LogError);
     } else {
         QByteArray ba1;
         appendByte(&ba1, 0x11); // options "step over"
@@ -2029,7 +1890,7 @@ void TrkGdbAdapter::handleDirectWrite5(const TrkResult &response)
 {
     logMessage("DIRECT WRITE5: " + response.toString());
     if (const int errorCode = response.errorCode()) {
-        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1");
+        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1", LogError);
     } else {
         // Restore PC
         sendTrkMessage(0x13, TrkCB(handleDirectWrite6),
@@ -2041,7 +1902,7 @@ void TrkGdbAdapter::handleDirectWrite6(const TrkResult &response)
 {
     logMessage("DIRECT WRITE6: " + response.toString());
     if (const int errorCode = response.errorCode()) {
-        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1");
+        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1", LogError);
     } else {
         // Restore memory
         sendTrkMessage(0x11, TrkCB(handleDirectWrite7),
@@ -2053,7 +1914,7 @@ void TrkGdbAdapter::handleDirectWrite7(const TrkResult &response)
 {
     logMessage("DIRECT WRITE7: " + response.toString());
     if (const int errorCode = response.errorCode()) {
-        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1");
+        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1", LogError);
     } else {
         // Check
         sendTrkMessage(0x10, TrkCB(handleDirectWrite8),
@@ -2065,7 +1926,7 @@ void TrkGdbAdapter::handleDirectWrite8(const TrkResult &response)
 {
     logMessage("DIRECT WRITE8: " + response.toString());
     if (const int errorCode = response.errorCode()) {
-        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1");
+        logMessage("ERROR: " + response.errorString() + "in handleDirectWrite1", LogError);
     } else {
         // Re-read registers
         sendTrkMessage(0x12,
diff --git a/src/plugins/debugger/gdb/trkgdbadapter.h b/src/plugins/debugger/gdb/trkgdbadapter.h
index c2af38308aecbcbd4ce365722859f1195a4c8657..b24c8cd1f9645b9ed46b98b322348dd19acbba30 100644
--- a/src/plugins/debugger/gdb/trkgdbadapter.h
+++ b/src/plugins/debugger/gdb/trkgdbadapter.h
@@ -33,19 +33,24 @@
 #include "abstractgdbadapter.h"
 
 #include "trkutils.h"
-#include "trkdevice.h"
-#include "launcher.h"
-#include "abstractgdbprocess.h"
+#include "callback.h"
+#include "symbian.h"
 
-#include <QtCore/QHash>
 #include <QtCore/QPointer>
 #include <QtCore/QSharedPointer>
-#include <QtCore/QQueue>
-#include <QtCore/QString>
 #include <QtCore/QStringList>
+#include <QtCore/QHash>
 
-#include <QtNetwork/QTcpServer>
-#include <QtNetwork/QTcpSocket>
+QT_BEGIN_NAMESPACE
+class QTcpServer;
+class QTcpSocket;
+QT_END_NAMESPACE
+
+namespace trk {
+struct TrkResult;
+struct TrkMessage;
+class TrkDevice;
+}
 
 namespace SymbianUtils {
 class SymbianDevice;
@@ -54,80 +59,8 @@ class SymbianDevice;
 namespace Debugger {
 namespace Internal {
 
-enum CodeMode
-{
-    ArmMode = 0,
-    ThumbMode,
-};
-
-enum TargetConstants
-{
-    RegisterCount = 17,
-    RegisterSP = 13, // Stack Pointer
-    RegisterLR = 14, // Return address
-    RegisterPC = 15, // Program counter
-    RegisterPSGdb = 25, // gdb's view of the world
-    RegisterPSTrk = 16, // TRK's view of the world
-
-    MemoryChunkSize = 256
-};
-
-struct MemoryRange
-{
-    MemoryRange() : from(0), to(0) {}
-    MemoryRange(uint f, uint t);
-    void operator-=(const MemoryRange &other);
-    bool intersects(const MemoryRange &other) const;
-    quint64 hash() const { return (quint64(from) << 32) + to; }
-    bool operator==(const MemoryRange &other) const { return hash() == other.hash(); }
-    bool operator<(const MemoryRange &other) const { return hash() < other.hash(); }
-    uint size() const { return to - from; }
-    bool isReadOnly() const;
-
-    uint from; // Inclusive.
-    uint to;   // Exclusive.
-};
-
-struct Snapshot
-{
-    Snapshot() { reset(); }
-
-    void reset(); // Leaves read-only memory cache alive.
-    void fullReset(); // Also removes read-only memory cache.
-    void insertMemory(const MemoryRange &range, const QByteArray &ba);
-
-    uint registers[RegisterCount];
-    bool registerValid;
-    typedef QMap<MemoryRange, QByteArray> Memory;
-    Memory memory;
-
-    // Current state.
-    MemoryRange wantedMemory;
-
-    // For next step.
-    uint lineFromAddress;
-    uint lineToAddress;
-    bool stepOver;
-};
-
-
-struct Breakpoint
-{
-    Breakpoint(uint offset_ = 0)
-    {
-        number = 0;
-        offset = offset_;
-        mode = ArmMode;
-    }
-    uint offset;
-    ushort number;
-    CodeMode mode;
-};
-
-struct GdbResult
-{
-    QByteArray data;
-};
+struct MemoryRange;
+struct GdbResult;
 
 ///////////////////////////////////////////////////////////////////////
 //
@@ -264,21 +197,6 @@ private:
     QSharedPointer<trk::TrkDevice> m_trkDevice;
     QString m_adapterFailMessage;
 
-    //
-    // Gdb
-    //
-    struct GdbCommand
-    {
-        GdbCommand() : flags(0), callback(GdbCallback()), callbackName(0) {}
-
-        int flags;
-        GdbCallback callback;
-        const char *callbackName;
-        QString command;
-        QVariant cookie;
-        //QTime postTime;
-    };
-
     Q_SLOT void handleGdbConnection();
     Q_SLOT void readGdbServerCommand();
     void readGdbResponse();
@@ -291,7 +209,7 @@ private:
     bool sendGdbServerPacket(const QByteArray &packet, bool doFlush);
     void tryAnswerGdbMemoryRequest(bool buffered);
 
-    void logMessage(const QString &msg);  // triggers output() if m_verbose
+    void logMessage(const QString &msg, int logChannel = LogDebug);  // triggers output() if m_verbose
     Q_SLOT void trkLogMessage(const QString &msg);
 
     QPointer<QTcpServer> m_gdbServer;
@@ -299,11 +217,9 @@ private:
     QByteArray m_gdbReadBuffer;
     bool m_gdbAckMode;
 
-    QHash<int, GdbCommand> m_gdbCookieForToken;
-
     // Debuggee state
     trk::Session m_session; // global-ish data (process id, target information)
-    Snapshot m_snapshot; // local-ish data (memory and registers)
+    Symbian::Snapshot m_snapshot; // local-ish data (memory and registers)
     QString m_remoteExecutable;
     QStringList m_remoteArguments;
     QString m_symbolFile;
@@ -315,6 +231,4 @@ private:
 } // namespace Internal
 } // namespace Debugger
 
-Q_DECLARE_METATYPE(Debugger::Internal::MemoryRange);
-
 #endif // DEBUGGER_TRKGDBADAPTER_H
diff --git a/src/shared/symbianutils/json.cpp b/src/shared/symbianutils/json.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4171125ee97e35cf6e71d3d52ff0965007b38442
--- /dev/null
+++ b/src/shared/symbianutils/json.cpp
@@ -0,0 +1,478 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 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"
+
+#ifdef TODO_USE_CREATOR
+#include <utils/qtcassert.h>
+#endif // TODO_USE_CREATOR
+
+#include <QtCore/QByteArray>
+#include <QtCore/QTextStream>
+#include <QtCore/QDebug>
+#include <QtCore/QStringList>
+
+#include <ctype.h>
+
+//#define DEBUG_JASON
+#ifdef DEBUG_JASON
+#define JDEBUG(s) qDebug() << s
+#else
+#define JDEBUG(s)
+#endif
+
+namespace tcftrk {
+
+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;
+    if (from < to && *from == '-') // Leading '-'.
+        result.append(*from++);
+    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 't':
+            if (to - from >= 4 && qstrncmp(from, "true", 4) == 0) {
+                m_data = QByteArray(from, 4);
+                from += m_data.size();
+                m_type = Boolean;
+            }
+            break;
+        case 'f':
+            if (to - from >= 5 && qstrncmp(from, "false", 5) == 0) {
+                m_data = QByteArray(from, 5);
+                from += m_data.size();
+                m_type = Boolean;
+            }
+            break;
+        case 'n':
+            if (to - from >= 4 && qstrncmp(from, "null", 4) == 0) {
+                m_data = QByteArray(from, 4);
+                from += m_data.size();
+                m_type = NullObject;
+            }
+            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':
+        case '-':
+            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));
+#ifdef TODO_USE_CREATOR
+    QTC_ASSERT(*from == '{', /**/);
+#endif
+    ++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));
+#ifdef TODO_USE_CREATOR
+    QTC_ASSERT(*from == '[', /**/);
+#endif
+    ++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 Boolean:
+        case NullObject:
+            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();
+}
+
+void JsonInputStream::appendCString(const char *s)
+{
+    m_target.append('"');
+    for (const char *p = s; *p; p++) {
+        if (*p == '"' || *p == '\\')
+            m_target.append('\\');
+        m_target.append(*p);
+    }
+    m_target.append('"');
+}
+
+void JsonInputStream::appendString(const QString &in)
+{
+    if (in.isEmpty()) {
+        m_target.append("\"\"");
+        return;
+    }
+
+    const QChar doubleQuote('"');
+    const QChar backSlash('\\');
+    QString rc;
+    const int inSize = in.size();
+    rc.reserve(in.size() + 5);
+    rc.append(doubleQuote);
+    for (int i = 0; i < inSize; i++) {
+        const QChar c = in.at(i);
+        if (c == doubleQuote || c == backSlash)
+            rc.append(backSlash);
+        rc.append(c);
+    }
+    rc.append(doubleQuote);
+    m_target.append(rc.toUtf8());
+    return;
+}
+
+JsonInputStream &JsonInputStream::operator<<(const QStringList &in)
+{
+    m_target.append('[');
+    const int count = in.size();
+    for (int i = 0 ; i < count; i++) {
+        if (i)
+            m_target.append(',');
+        appendString(in.at(i));
+    }
+    m_target.append(']');
+    return *this;
+}
+
+JsonInputStream &JsonInputStream::operator<<(const QVector<QByteArray> &ba)
+{
+    m_target.append('[');
+    const int count = ba.size();
+    for (int i = 0 ; i < count; i++) {
+        if (i)
+            m_target.append(',');
+        appendCString(ba.at(i).constData());
+    }
+    m_target.append(']');
+    return *this;
+}
+
+JsonInputStream &JsonInputStream::operator<<(bool b)
+{
+    m_target.append(b ? "true" : "false");
+    return *this;
+}
+
+} // namespace tcftrk
+
diff --git a/src/shared/symbianutils/json.h b/src/shared/symbianutils/json.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef574bdae249ab3432bf30e18e02c109c869a69f
--- /dev/null
+++ b/src/shared/symbianutils/json.h
@@ -0,0 +1,137 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 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 SYMBIANUTILS_JSON_H
+#define SYMBIANUTILS_JSON_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+namespace tcftrk {
+
+class SYMBIANUTILS_EXPORT 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,
+        Boolean,
+        Object,
+        NullObject,
+        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;
+};
+
+/* Thin wrapper around QByteArray for formatting JSON input. Use as in:
+ * JsonInputStream(byteArray) << '{' <<  "bla" << ':' << "blup" << '}';
+ * Note that strings get double quotes and JSON-escaping, characters should be
+ * used for the array/hash delimiters.
+ * */
+class SYMBIANUTILS_EXPORT JsonInputStream {
+public:
+    explicit JsonInputStream(QByteArray &a) : m_target(a) {}
+
+    JsonInputStream &operator<<(char c) {  m_target.append(c); return *this; }
+    JsonInputStream &operator<<(const char *c)  { appendCString(c); return *this; }
+    JsonInputStream &operator<<(const QByteArray &a)  { appendCString(a.constData()); return *this; }
+    JsonInputStream &operator<<(const QString &c) { appendString(c); return *this; }
+
+    // Format as array
+    JsonInputStream &operator<<(const QStringList &c);
+
+    // Format as array
+    JsonInputStream &operator<<(const QVector<QByteArray> &ba);
+
+    JsonInputStream &operator<<(bool b);
+
+    JsonInputStream &operator<<(int i)
+        { m_target.append(QByteArray::number(i)); return *this; }
+    JsonInputStream &operator<<(unsigned i)
+        { m_target.append(QByteArray::number(i)); return *this; }
+    JsonInputStream &operator<<(quint64 i)
+        { m_target.append(QByteArray::number(i)); return *this; }
+
+private:
+    void appendString(const QString &);
+    void appendCString(const char *c);
+
+    QByteArray &m_target;
+};
+
+} // namespace tcftrk
+
+#endif // SYMBIANUTILS_JSON_H
diff --git a/src/shared/symbianutils/symbianutils.pri b/src/shared/symbianutils/symbianutils.pri
index 6309517b18310e117dc2abeb1c7954eeae179fdc..f07e494ed1764d738844a7ad331ce4d770e0c757 100644
--- a/src/shared/symbianutils/symbianutils.pri
+++ b/src/shared/symbianutils/symbianutils.pri
@@ -1,5 +1,7 @@
 INCLUDEPATH *= $$PWD
 
+QT += network
+
 # Input
 HEADERS += $$PWD/symbianutils_global.h \
     $$PWD/callback.h \
@@ -9,14 +11,20 @@ HEADERS += $$PWD/symbianutils_global.h \
     $$PWD/launcher.h \
     $$PWD/bluetoothlistener.h \
     $$PWD/communicationstarter.h \
-    $$PWD/symbiandevicemanager.h
+    $$PWD/symbiandevicemanager.h \
+    $$PWD/tcftrkdevice.h \
+    $$PWD/tcftrkmessage.h \
+    $$PWD/json.h
 
 SOURCES += $$PWD/trkutils.cpp \
     $$PWD/trkdevice.cpp \
     $$PWD/launcher.cpp \
     $$PWD/bluetoothlistener.cpp \
     $$PWD/communicationstarter.cpp \
-    $$PWD/symbiandevicemanager.cpp
+    $$PWD/symbiandevicemanager.cpp \
+    $$PWD/tcftrkdevice.cpp \
+    $$PWD/tcftrkmessage.cpp \
+    $$PWD/json.cpp
 
 # Tests/trklauncher is a console application
 contains(QT, gui) {
diff --git a/src/shared/symbianutils/tcftrkdevice.cpp b/src/shared/symbianutils/tcftrkdevice.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9f646e3ec8348ceef23613b75ffe503cafeae379
--- /dev/null
+++ b/src/shared/symbianutils/tcftrkdevice.cpp
@@ -0,0 +1,889 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 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 "tcftrkdevice.h"
+#include "json.h"
+
+#include <QtNetwork/QAbstractSocket>
+#include <QtCore/QDebug>
+#include <QtCore/QVector>
+#include <QtCore/QQueue>
+#include <QtCore/QTextStream>
+#include <QtCore/QDateTime>
+
+enum { debug = 0 };
+
+static const char messageTerminatorC[] = "\003\001";
+
+namespace tcftrk {
+// ------------- TcfTrkCommandError
+
+TcfTrkCommandError::TcfTrkCommandError() : timeMS(0), code(0), alternativeCode(0)
+{
+}
+
+void TcfTrkCommandError::clear()
+{
+    timeMS = 0;
+    code = alternativeCode = 0;
+    format.clear();
+    alternativeOrganization.clear();
+}
+
+void TcfTrkCommandError::write(QTextStream &str) const
+{
+    if (timeMS) {
+        const QDateTime time(QDate(1970, 1, 1));
+        str << time.addMSecs(timeMS).toString(Qt::ISODate) << ": Error code: " << code
+                << " '" << format << '\'';
+        if (!alternativeOrganization.isEmpty())
+            str << " ('" << alternativeOrganization << "', code: " << alternativeCode << ')';
+    } else{
+        str << "<No error>";
+    }
+}
+
+QString TcfTrkCommandError::toString() const
+{
+    QString rc;
+    QTextStream str(&rc);
+    write(str);
+    return rc;
+}
+
+/* {"Time":1277459762255,"Code":1,"AltCode":-6,"AltOrg":"POSIX","Format":"Unknown error: -6"} */
+bool TcfTrkCommandError::parse(const QVector<JsonValue> &values)
+{
+    // Parse an arbitrary hash (that could as well be a command response)
+    // and check for error elements.
+    unsigned errorKeyCount = 0;
+    clear();
+    do {
+        if (values.isEmpty() || values.front().type() != JsonValue::Object)
+            break;
+        foreach (const JsonValue &c, values.front().children()) {
+            if (c.name() == "Time") {
+                timeMS = c.data().toULongLong();
+                errorKeyCount++;
+            } else if (c.name() == "Code") {
+                code = c.data().toInt();
+                errorKeyCount++;
+            } else if (c.name() == "Format") {
+                format = c.data();
+                errorKeyCount++;
+            } else if (c.name() == "AltCode") {
+                alternativeCode = c.data().toInt();
+                errorKeyCount++;
+            } else if (c.name() == "AltOrg") {
+                alternativeOrganization = c.data();
+                errorKeyCount++;
+            }
+        }
+    } while (false);
+    const bool errorFound = errorKeyCount >= 2u; // Should be at least 'Time', 'Code'.
+    if (!errorFound)
+        clear();
+    if (debug) {
+        qDebug() << "TcfTrkCommandError::parse: Found error: " << errorFound;
+        if (!values.isEmpty())
+            qDebug() << values.front().toString();
+    }
+    return errorFound;
+}
+
+// ------------ TcfTrkCommandResult
+
+TcfTrkCommandResult::TcfTrkCommandResult(Type t) :
+    type(t), service(LocatorService)
+{
+}
+
+TcfTrkCommandResult::TcfTrkCommandResult(char typeChar, Services s,
+                                         const QByteArray &r,
+                                         const QVector<JsonValue> &v,
+                                         const QVariant &ck) :
+    type(FailReply), service(s), request(r), values(v), cookie(ck)
+{
+    switch (typeChar) {
+    case 'N':
+        type = FailReply;
+        break;
+    case 'P':
+        type = ProgressReply;
+        break;
+    case 'R':
+        type = commandError.parse(values) ? CommandErrorReply : SuccessReply;
+        break;
+    default:
+        qWarning("Unknown TCF reply type '%c'", typeChar);
+    }
+}
+
+QString TcfTrkCommandResult::errorString() const
+{
+    QString rc;
+    QTextStream str(&rc);
+
+    switch (type) {
+    case SuccessReply:
+    case ProgressReply:
+        str << "<No error>";
+        return rc;
+    case FailReply:
+        str << "NAK";
+    case CommandErrorReply:
+        commandError.write(str);
+        break;
+    }
+    // Append the failed command for reference
+    str << " (Command was: '";
+    QByteArray printableRequest = request;
+    printableRequest.replace('\0', '|');
+    str << printableRequest << "')";
+    return rc;
+}
+
+QString TcfTrkCommandResult::toString() const
+{
+    QString rc;
+    QTextStream str(&rc);
+    str << "Command answer ";
+    switch (type) {
+    case SuccessReply:
+        str << "[success]";
+        break;
+    case CommandErrorReply:
+        str << "[command error]";
+        break;
+    case FailReply:
+        str << "[fail (NAK)]";
+        break;
+    case ProgressReply:
+        str << "[progress]";
+        break;
+    }
+    str << ", " << values.size() << " values(s) to request: '";
+    QByteArray printableRequest = request;
+    printableRequest.replace('\0', '|');
+    str << printableRequest << "' ";
+    if (cookie.isValid())
+        str << " cookie: " << cookie.toString();
+    str << '\n';
+    for (int i = 0, count = values.size(); i < count; i++)
+        str << '#' << i << ' ' << values.at(i).toString() << '\n';
+    if (type == CommandErrorReply)
+        str << "Error: " << errorString();
+    return rc;
+}
+
+struct TcfTrkSendQueueEntry
+{
+    typedef TcfTrkDevice::MessageType MessageType;
+
+    explicit TcfTrkSendQueueEntry(MessageType mt,
+                                  int tok,
+                                  Services s,
+                           const QByteArray &d,
+                           const TcfTrkCallback &cb= TcfTrkCallback(),
+                           const QVariant &ck = QVariant()) :
+    messageType(mt), service(s), data(d), token(tok), cookie(ck), callback(cb)  {}
+
+    MessageType messageType;
+    Services service;
+    QByteArray data;
+    int token;
+    QVariant cookie;
+    TcfTrkCallback callback;
+};
+
+struct TcfTrkDevicePrivate {
+    typedef TcfTrkDevice::IODevicePtr IODevicePtr;
+    typedef QHash<int, TcfTrkSendQueueEntry> TokenWrittenMessageMap;
+
+    TcfTrkDevicePrivate();
+
+    const QByteArray m_messageTerminator;
+
+    IODevicePtr m_device;
+    unsigned m_verbose;
+    QByteArray m_readBuffer;
+    int m_token;
+    QQueue<TcfTrkSendQueueEntry> m_sendQueue;
+    TokenWrittenMessageMap m_writtenMessages;
+    QVector<QByteArray> m_registerNames;
+};
+
+TcfTrkDevicePrivate::TcfTrkDevicePrivate() :
+    m_messageTerminator(messageTerminatorC),
+    m_verbose(0), m_token(0)
+{
+}
+
+TcfTrkDevice::TcfTrkDevice(QObject *parent) :
+    QObject(parent), d(new TcfTrkDevicePrivate)
+{
+}
+
+TcfTrkDevice::~TcfTrkDevice()
+{
+    delete d;
+}
+
+QVector<QByteArray> TcfTrkDevice::registerNames() const
+{
+    return d->m_registerNames;
+}
+
+void TcfTrkDevice::setRegisterNames(const QVector<QByteArray>& n)
+{
+    d->m_registerNames = n;
+    if (d->m_verbose) {
+        QString msg;
+        QTextStream str(&msg);
+        const int count = n.size();
+        str << "Registers (" << count << "): ";
+        for (int i = 0; i < count; i++)
+            str << '#' << i << '=' << n.at(i) << ' ';
+        emitLogMessage(msg);
+    }
+}
+
+TcfTrkDevice::IODevicePtr TcfTrkDevice::device() const
+{
+    return d->m_device;
+}
+
+TcfTrkDevice::IODevicePtr TcfTrkDevice::takeDevice()
+{
+    const IODevicePtr old = d->m_device;
+    if (!old.isNull()) {
+        old.data()->disconnect(this);
+        d->m_device = IODevicePtr();
+    }
+    d->m_readBuffer.clear();
+    d->m_token = 0;
+    d->m_sendQueue.clear();
+    return old;
+}
+
+void TcfTrkDevice::setDevice(const IODevicePtr &dp)
+{
+    if (dp.data() == d->m_device.data())
+        return;
+    if (dp.isNull()) {
+        emitLogMessage(QLatin1String("Internal error: Attempt to set NULL device."));
+        return;
+    }
+    takeDevice();
+    d->m_device = dp;
+    connect(dp.data(), SIGNAL(readyRead()), this, SLOT(slotDeviceReadyRead()));
+    if (QAbstractSocket *s = qobject_cast<QAbstractSocket *>(dp.data())) {
+        connect(s, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotDeviceError()));
+        connect(s, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(slotDeviceSocketStateChanged()));
+    }
+}
+
+void TcfTrkDevice::slotDeviceError()
+{
+    const QString message = d->m_device->errorString();
+    emitLogMessage(message);
+    emit error(message);
+}
+
+void TcfTrkDevice::slotDeviceSocketStateChanged()
+{
+    if (const QAbstractSocket *s = qobject_cast<const QAbstractSocket *>(d->m_device.data())) {
+        const QAbstractSocket::SocketState st = s->state();
+        switch (st) {
+        case QAbstractSocket::UnconnectedState:
+            emitLogMessage(QLatin1String("Unconnected"));
+            break;
+        case QAbstractSocket::HostLookupState:
+            emitLogMessage(QLatin1String("HostLookupState"));
+            break;
+        case QAbstractSocket::ConnectingState:
+            emitLogMessage(QLatin1String("Connecting"));
+            break;
+        case QAbstractSocket::ConnectedState:
+            emitLogMessage(QLatin1String("Connected"));
+            break;
+        case QAbstractSocket::ClosingState:
+            emitLogMessage(QLatin1String("Closing"));
+            break;
+        default:
+            emitLogMessage(QString::fromLatin1("State %1").arg(st));
+            break;
+        }
+    }
+}
+
+static inline QString debugMessage(QByteArray  message, const char *prefix = 0)
+{
+    message.replace('\0', '|');
+    const QString messageS = QString::fromLatin1(message);
+    return prefix ?
+            (QLatin1String(prefix) + messageS) :  messageS;
+}
+
+void TcfTrkDevice::slotDeviceReadyRead()
+{
+    d->m_readBuffer += d->m_device->readAll();
+    // Take complete message off front of readbuffer.
+    do {
+        const int messageEndPos = d->m_readBuffer.indexOf(d->m_messageTerminator);
+        if (messageEndPos == -1)
+            break;
+        const QByteArray message = d->m_readBuffer.left(messageEndPos);
+        if (debug)
+            qDebug("Read:\n%s", qPrintable(formatData(message)));
+        if (const int errorCode = parseMessage(message)) {
+            emitLogMessage(QString::fromLatin1("Parse error %1 for: %2").arg(errorCode).arg(debugMessage(message)));
+        }
+        d->m_readBuffer.remove(0, messageEndPos + d->m_messageTerminator.size());
+    } while (!d->m_readBuffer.isEmpty());
+    checkSendQueue(); // Send off further message
+}
+
+// Split \0-terminated message into tokens, skipping the initial type character
+static inline QVector<QByteArray> splitMessage(const QByteArray &message)
+{
+    QVector<QByteArray> tokens;
+    tokens.reserve(7);
+    const int messageSize = message.size();
+    for (int pos = 2; pos < messageSize; ) {
+        const int nextPos = message.indexOf('\0', pos);
+        if (nextPos == -1)
+            break;
+        tokens.push_back(message.mid(pos, nextPos - pos));
+        pos = nextPos + 1;
+    }
+    return tokens;
+}
+
+int TcfTrkDevice::parseMessage(const QByteArray &message)
+{
+    if (d->m_verbose)
+        emitLogMessage(debugMessage(message, "TCF ->"));
+    // Special JSON parse error message or protocol format error.
+    // The port is usually closed after receiving it.
+    // "\3\2{"Time":1276096098255,"Code":3,"Format": "Protocol format error"}"
+    if (message.startsWith("\003\002")) {
+        QByteArray text = message.mid(2);
+        const QString errorMessage = QString::fromLatin1("Parse error received: %1").arg(QString::fromAscii(text));
+        emit error(errorMessage);
+        return 0;
+    }
+    if (message.size() < 4 || message.at(1) != '\0')
+        return 1;
+    // Split into tokens
+    const char type = message.at(0);
+    const QVector<QByteArray> tokens = splitMessage(message);
+    switch (type) {
+    case 'E':
+        return parseTcfEvent(tokens);
+    case 'R': // Command replies
+    case 'N':
+    case 'P':
+        return parseTcfCommandReply(type, tokens);
+    default:
+        emitLogMessage(QString::fromLatin1("Unhandled message type: %1").arg(debugMessage(message)));
+        return 756;
+    }
+    return 0;
+}
+
+int TcfTrkDevice::parseTcfCommandReply(char type, const QVector<QByteArray> &tokens)
+{
+    typedef TcfTrkDevicePrivate::TokenWrittenMessageMap::iterator TokenWrittenMessageMapIterator;
+    // Find the corresponding entry in the written messages hash.
+    const int tokenCount = tokens.size();
+    if (tokenCount < 1)
+        return 234;
+    bool tokenOk;
+    const int token = tokens.at(0).toInt(&tokenOk);
+    if (!tokenOk)
+        return 235;
+    const TokenWrittenMessageMapIterator it = d->m_writtenMessages.find(token);
+    if (it == d->m_writtenMessages.end()) {
+        qWarning("TcfTrkDevice: Internal error: token %d not found for '%s'",
+                 token, qPrintable(joinByteArrays(tokens)));
+        return 236;
+    }
+    // No callback: remove entry from map, happy
+    if (!it.value().callback) {
+        d->m_writtenMessages.erase(it);
+        return 0;
+    }
+    // Parse values into JSON
+    QVector<JsonValue> values;
+    values.reserve(tokenCount);
+    for (int i = 1; i < tokenCount; i++) {
+        if (!tokens.at(i).isEmpty()) { // Strange: Empty tokens occur.
+            const JsonValue value(tokens.at(i));
+            if (value.isValid()) {
+                values.push_back(value);
+            } else {
+                qWarning("JSON parse error for reply to command token %d: #%d '%s'",
+                         token, i, tokens.at(i).constData());
+                d->m_writtenMessages.erase(it);
+                return -1;
+            }
+        }
+    }
+
+    // Construct result and invoke callback, remove entry from map.
+    TcfTrkCallback callback = it.value().callback;
+    TcfTrkCommandResult result(type, it.value().service, it.value().data,
+                               values, it.value().cookie);
+    d->m_writtenMessages.erase(it);
+    callback(result);
+    return 0;
+}
+
+static const char locatorAnswerC[] = "E\0Locator\0Hello\0[\"Locator\"]";
+
+int TcfTrkDevice::parseTcfEvent(const QVector<QByteArray> &tokens)
+{
+    // Event: Ignore the periodical heartbeat event, answer 'Hello',
+    // emit signal for the rest
+    if (tokens.size() < 3)
+        return 433;
+    const Services service = serviceFromName(tokens.at(0).constData());
+    if (service == LocatorService && tokens.at(1) == "peerHeartBeat")
+        return 0;
+    QVector<JsonValue> values;
+    for (int i = 2; i < tokens.size(); i++) {
+        const JsonValue value(tokens.at(i));
+        if (!value.isValid())
+            return 434;
+        values.push_back(value);
+    }
+    // Parse known events, emit signals
+    QScopedPointer<TcfTrkEvent> knownEvent(TcfTrkEvent::parseEvent(service, tokens.at(1), values));
+    if (!knownEvent.isNull()) {
+        // Answer hello event.
+        if (knownEvent->type() == TcfTrkEvent::LocatorHello)
+            writeMessage(QByteArray(locatorAnswerC, sizeof(locatorAnswerC)));
+        emit tcfEvent(*knownEvent);
+    }
+    emit genericTcfEvent(service, tokens.at(1), values);
+
+    if (debug || d->m_verbose) {
+        QString msg;
+        QTextStream str(&msg);
+        if (knownEvent.isNull()) {
+            str << "Event: " << tokens.at(0) << ' ' << tokens.at(1) << '\n';
+            foreach(const JsonValue &val, values)
+                str << "  " << val.toString() << '\n';
+        } else {
+            str << knownEvent->toString();
+        }
+        emitLogMessage(msg);
+    }
+
+    return 0;
+}
+
+unsigned TcfTrkDevice::verbose() const
+{
+    return d->m_verbose;
+}
+
+void TcfTrkDevice::setVerbose(unsigned v)
+{
+    d->m_verbose = v;
+}
+
+void TcfTrkDevice::emitLogMessage(const QString &m)
+{
+    if (debug)
+        qWarning("%s", qPrintable(m));
+    emit logMessage(m);
+}
+
+bool TcfTrkDevice::checkOpen()
+{
+    if (d->m_device.isNull()) {
+        emitLogMessage(QLatin1String("Internal error: No device set on TcfTrkDevice."));
+        return false;
+    }
+    if (!d->m_device->isOpen()) {
+        emitLogMessage(QLatin1String("Internal error: Device not open in TcfTrkDevice."));
+        return false;
+    }
+    return true;
+}
+
+void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+                                     const char *commandParameters, int commandParametersLength,
+                                     const TcfTrkCallback &callBack,
+                                     const QVariant &cookie)
+
+{
+    if (!checkOpen())
+        return;
+    // Format the message
+    const int  token = d->m_token++;
+    QByteArray data;
+    data.reserve(30 + commandParametersLength);
+    data.append('C');
+    data.append('\0');
+    data.append(QByteArray::number(token));
+    data.append('\0');
+    data.append(serviceName(service));
+    data.append('\0');
+    data.append(command);
+    data.append('\0');
+    if (commandParametersLength)
+        data.append(commandParameters, commandParametersLength);
+    const TcfTrkSendQueueEntry entry(mt, token, service, data, callBack, cookie);
+    d->m_sendQueue.enqueue(entry);
+    checkSendQueue();
+}
+
+void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+                                     const QByteArray &commandParameters,
+                                     const TcfTrkCallback &callBack,
+                                     const QVariant &cookie)
+{
+    sendTcfTrkMessage(mt, service, command, commandParameters.constData(), commandParameters.size(),
+                      callBack, cookie);
+}
+
+// Enclose in message frame and write.
+void TcfTrkDevice::writeMessage(QByteArray data)
+{
+    if (!checkOpen())
+        return;
+
+    if (d->m_verbose)
+        emitLogMessage(debugMessage(data, "TCF <-"));
+
+    // Ensure \0-termination which easily gets lost in QByteArray CT.
+    if (!data.endsWith('\0'))
+        data.append('\0');
+    data += d->m_messageTerminator;
+
+    if (debug > 1)
+        qDebug("Writing:\n%s", qPrintable(formatData(data)));
+
+    d->m_device->write(data);
+    if (QAbstractSocket *as = qobject_cast<QAbstractSocket *>(d->m_device.data()))
+        as->flush();
+}
+
+void TcfTrkDevice::checkSendQueue()
+{
+    // Fire off messages or invoke noops until a message with reply is found
+    // and an entry to writtenMessages is made.
+    while (d->m_writtenMessages.empty()) {
+        if (d->m_sendQueue.isEmpty())
+            break;
+        TcfTrkSendQueueEntry entry = d->m_sendQueue.dequeue();
+        switch (entry.messageType) {
+        case MessageWithReply:
+            d->m_writtenMessages.insert(entry.token, entry);
+            writeMessage(entry.data);
+            break;
+        case MessageWithoutReply:
+            writeMessage(entry.data);
+            break;
+        case NoopMessage: // Invoke the noop-callback for synchronization
+            if (entry.callback) {
+                TcfTrkCommandResult noopResult(TcfTrkCommandResult::SuccessReply);
+                noopResult.cookie = entry.cookie;
+                entry.callback(noopResult);
+            }
+            break;
+        }
+    }
+}
+
+// Fix slashes
+static inline QString fixFileName(QString in)
+{
+    in.replace(QLatin1Char('/'), QLatin1Char('\\'));
+    return in;
+}
+
+// Start a process (consisting of a non-reply setSettings and start).
+void TcfTrkDevice::sendProcessStartCommand(const TcfTrkCallback &callBack,
+                                                 const QString &binaryIn,
+                                                 unsigned uid,
+                                                 QStringList arguments,
+                                                 QString workingDirectory,
+                                                 bool debugControl,
+                                                 const QStringList &additionalLibraries,
+                                                 const QVariant &cookie)
+{
+    // Obtain the bin directory, expand by c:/sys/bin if missing
+    const QChar backSlash('\\');
+    int slashPos = binaryIn.lastIndexOf(QLatin1Char('/'));
+    if (slashPos == -1)
+        slashPos = binaryIn.lastIndexOf(backSlash);
+    const QString sysBin = QLatin1String("c:/sys/bin");
+    const QString binaryFileName  = slashPos == -1 ? binaryIn : binaryIn.mid(slashPos + 1);
+    const QString binaryDirectory = slashPos == -1 ? sysBin : binaryIn.left(slashPos);
+    const QString binary = fixFileName(binaryDirectory + QLatin1Char('/') + binaryFileName);
+
+    // Fixup: Does argv[0] convention exist on Symbian?
+    arguments.push_front(binary);
+    if (workingDirectory.isEmpty())
+        workingDirectory = sysBin;
+
+    // Format settings with empty dummy parameter
+    QByteArray setData;
+    JsonInputStream setStr(setData);
+    setStr << "" << '\0'
+            << '[' << "exeToLaunch" << ',' << "addExecutables" << ',' << "addLibraries" << ']'
+            << '\0' << '['
+                << binary << ','
+                << '{' << binaryFileName << ':' << QString::number(uid, 16) << '}' << ','
+                << additionalLibraries
+            << ']';
+    sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData);
+
+    QByteArray startData;
+    JsonInputStream startStr(startData);
+    startStr << fixFileName(workingDirectory)
+            << '\0' << binary << '\0' << arguments << '\0'
+            << QStringList() << '\0' // Env is an array ["PATH=value"] (non-standard)
+            << debugControl;
+    sendTcfTrkMessage(MessageWithReply, ProcessesService, "start", startData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendProcessTerminateCommand(const TcfTrkCallback &callBack,
+                                               const QByteArray &id,
+                                               const QVariant &cookie)
+{
+    QByteArray data;
+    JsonInputStream str(data);
+    str << id;
+    sendTcfTrkMessage(MessageWithReply, ProcessesService, "terminate", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+                                               const QByteArray &id,
+                                               RunControlResumeMode mode,
+                                               unsigned count,
+                                               quint64 rangeStart,
+                                               quint64 rangeEnd,
+                                               const QVariant &cookie)
+{
+    QByteArray resumeData;
+    JsonInputStream str(resumeData);
+    str << id << '\0' << int(mode) << '\0' << count;
+    switch (mode) {
+    case RM_STEP_OVER_RANGE:
+    case RM_STEP_INTO_RANGE:
+    case RM_REVERSE_STEP_OVER_RANGE:
+    case RM_REVERSE_STEP_INTO_RANGE:
+        str << '\0' << '{' << "RANGE_START" << ':' << rangeStart
+                << ',' << "RANGE_END" << ':' << rangeEnd << '}';
+        break;
+    default:
+        break;
+    }
+    sendTcfTrkMessage(MessageWithReply, RunControlService, "resume", resumeData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
+                                                const QByteArray &id,
+                                                const QVariant &cookie)
+{
+    QByteArray data;
+    JsonInputStream str(data);
+    str << id;
+    sendTcfTrkMessage(MessageWithReply, RunControlService, "suspend", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+                                               const QByteArray &id,
+                                               const QVariant &cookie)
+{
+    sendRunControlResumeCommand(callBack, id, RM_RESUME, 1, 0, 0, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
+                                             const Breakpoint &bp,
+                                             const QVariant &cookie)
+{
+    QByteArray data;
+    JsonInputStream str(data);
+    str << bp;
+    sendTcfTrkMessage(MessageWithReply, BreakpointsService, "add", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+                                                const QByteArray &id,
+                                                const QVariant &cookie)
+{
+    sendBreakpointsRemoveCommand(callBack, QVector<QByteArray>(1, id), cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+                                                const QVector<QByteArray> &ids,
+                                                const QVariant &cookie)
+{
+    QByteArray data;
+    JsonInputStream str(data);
+    str << ids;
+    sendTcfTrkMessage(MessageWithReply, BreakpointsService, "remove", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+                                                const QByteArray &id,
+                                                bool enable,
+                                                const QVariant &cookie)
+{
+    sendBreakpointsEnableCommand(callBack, QVector<QByteArray>(1, id), enable, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+                                                const QVector<QByteArray> &ids,
+                                                bool enable,
+                                                const QVariant &cookie)
+{
+    QByteArray data;
+    JsonInputStream str(data);
+    str << ids;
+    sendTcfTrkMessage(MessageWithReply, BreakpointsService,
+                      enable ? "enable" : "disable",
+                      data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendMemorySetCommand(const TcfTrkCallback &callBack,
+                                        const QByteArray &contextId,
+                                        quint64 start, const QByteArray& data,
+                                        const QVariant &cookie)
+{
+    QByteArray getData;
+    JsonInputStream str(getData);
+    // start/word size/mode. Mode should ideally be 1 (continue on error?)
+    str << contextId << '\0' << start << '\0' << 1 << '\0' << data.size() << '\0' << 1
+        << '\0' << data.toBase64();
+    sendTcfTrkMessage(MessageWithReply, MemoryService, "set", getData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendMemoryGetCommand(const TcfTrkCallback &callBack,
+                                        const QByteArray &contextId,
+                                        quint64 start, quint64 size,
+                                        const QVariant &cookie)
+{
+    QByteArray data;
+    JsonInputStream str(data);
+    // start/word size/mode. Mode should ideally be 1 (continue on error?)
+    str << contextId << '\0' << start << '\0' << 1 << '\0' << size << '\0' << 1;
+    sendTcfTrkMessage(MessageWithReply, MemoryService, "get", data, callBack, cookie);
+}
+
+QByteArray TcfTrkDevice::parseMemoryGet(const TcfTrkCommandResult &r)
+{
+    if (r.type != TcfTrkCommandResult::SuccessReply || r.values.size() < 1)
+        return QByteArray();
+    const JsonValue &memoryV = r.values.front();
+
+    if (memoryV.type() != JsonValue::String || memoryV.data().size() < 2
+        || !memoryV.data().endsWith('='))
+        return QByteArray();
+    // Catch errors reported as hash:
+    // R.4."TlVMTA==".{"Time":1276786871255,"Code":1,"AltCode":-38,"AltOrg":"POSIX","Format":"BadDescriptor"}
+    // Not sure what to make of it.
+    if (r.values.size() >= 2 && r.values.at(1).type() == JsonValue::Object)
+        qWarning("Error retrieving memory: %s", r.values.at(1).toString(false).constData());
+    // decode
+    const QByteArray memory = QByteArray::fromBase64(memoryV.data());
+    if (memory.isEmpty())
+        qWarning("Base64 decoding of %s failed.", memoryV.data().constData());
+    if (debug)
+        qDebug("TcfTrkDevice::parseMemoryGet: received %d bytes", memory.size());
+    return memory;
+}
+
+void TcfTrkDevice::sendRegistersGetMCommand(const TcfTrkCallback &callBack,
+                                            const QByteArray &contextId,
+                                            const QVector<QByteArray> &ids,
+                                            const QVariant &cookie)
+{
+    // TODO: use "Registers" (which uses base64-encoded values)
+    QByteArray data;
+    JsonInputStream str(data);
+    str << contextId << '\0' << ids;
+    sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "get", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
+                                                 const QByteArray &contextId,
+                                                 unsigned start, unsigned count)
+{
+    const unsigned end = start + count;
+    if (end > (unsigned)d->m_registerNames.size()) {
+        qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", end, d->m_registerNames.size());
+        return;
+    }
+
+    QVector<QByteArray> ids;
+    ids.reserve(count);
+    for (unsigned i = start; i < end; i++)
+        ids.push_back(d->m_registerNames.at(i));
+    sendRegistersGetMCommand(callBack, contextId, ids, QVariant(start));
+}
+
+// Set register
+void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
+                                           const QByteArray &contextId,
+                                           const QByteArray &id,
+                                           unsigned value,
+                                           const QVariant &cookie)
+{
+    // TODO: use "Registers" (which uses base64-encoded values)
+    QByteArray data;
+    JsonInputStream str(data);
+    str << contextId << '\0' << QVector<QByteArray>(1, id)
+            << '\0' << QVector<QByteArray>(1, QByteArray::number(value, 16));
+    sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "set", data, callBack, cookie);
+}
+
+// Set register
+void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
+                                           const QByteArray &contextId,
+                                           unsigned registerNumber,
+                                           unsigned value,
+                                           const QVariant &cookie)
+{
+    if (registerNumber >= (unsigned)d->m_registerNames.size()) {
+        qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", registerNumber, d->m_registerNames.size());
+        return;
+    }
+    sendRegistersSetCommand(callBack, contextId,
+                            d->m_registerNames[registerNumber],
+                            value, cookie);
+}
+
+} // namespace tcftrk
diff --git a/src/shared/symbianutils/tcftrkdevice.h b/src/shared/symbianutils/tcftrkdevice.h
new file mode 100644
index 0000000000000000000000000000000000000000..be5077b2a0823895814eecab282603d031c2e1d1
--- /dev/null
+++ b/src/shared/symbianutils/tcftrkdevice.h
@@ -0,0 +1,271 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 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 TCFTRKENGINE_H
+#define TCFTRKENGINE_H
+
+#include "symbianutils_global.h"
+#include "tcftrkmessage.h"
+#include "callback.h"
+#include "json.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QVector>
+#include <QtCore/QVariant>
+#include <QtCore/QStringList>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace tcftrk {
+
+struct TcfTrkDevicePrivate;
+struct Breakpoint;
+
+/* Command error handling in TCF:
+ * 1) 'Severe' errors (JSON format, parameter format): Trk emits a
+ *     nonstandard message (\3\2 error paramaters) and closes the connection.
+ * 2) Protocol errors: 'N' without error message is returned.
+ * 3) Errors in command execution: 'R' with a TCF error hash is returned
+ *    (see TcfTrkCommandError). */
+
+/* Error code return in 'R' reply to command
+ * (see top of 'Services' documentation). */
+struct SYMBIANUTILS_EXPORT TcfTrkCommandError {
+    TcfTrkCommandError();
+    void clear();
+    operator bool() const { return timeMS != 0; }
+    QString toString() const;
+    void write(QTextStream &str) const;
+    bool parse(const QVector<JsonValue> &values);
+
+    quint64 timeMS; // Since 1.1.1970
+    int code;
+    QByteArray format; // message
+    // 'Alternative' meaning, like altOrg="POSIX"/altCode=<some errno>
+    QByteArray alternativeOrganization;
+    int alternativeCode;
+};
+
+/* Answer to a Tcf command passed to the callback. */
+struct SYMBIANUTILS_EXPORT TcfTrkCommandResult {
+    enum Type {
+        SuccessReply,       // 'R' and no error -> all happy.
+        CommandErrorReply,  // 'R' with TcfTrkCommandError received
+        ProgressReply,      // 'P', progress indicator
+        FailReply           // 'N' Protocol NAK, severe error
+    };
+
+    explicit TcfTrkCommandResult(Type t = SuccessReply);
+    explicit TcfTrkCommandResult(char typeChar, Services service,
+                                 const QByteArray &request,
+                                 const QVector<JsonValue> &values,
+                                 const QVariant &cookie);
+
+    QString toString() const;
+    QString errorString() const;
+    operator bool() const { return type == SuccessReply || type == ProgressReply; }
+
+    Type type;
+    Services service;
+    QByteArray request;
+    TcfTrkCommandError commandError;
+    QVector<JsonValue> values;
+    QVariant cookie;
+};
+
+typedef trk::Callback<const TcfTrkCommandResult &> TcfTrkCallback;
+
+/* TcfTrkDevice: TCF communication helper using an asynchronous QIODevice
+ * implementing the TCF protocol according to:
+http://dev.eclipse.org/svnroot/dsdp/org.eclipse.tm.tcf/trunk/docs/TCF%20Specification.html
+http://dev.eclipse.org/svnroot/dsdp/org.eclipse.tm.tcf/trunk/docs/TCF%20Services.html
+ * Commands can be sent along with callbacks that are passed a
+ * TcfTrkCommandResult and an opaque QVariant cookie. In addition, events are emitted.
+*/
+
+class SYMBIANUTILS_EXPORT TcfTrkDevice : public QObject
+{
+    Q_PROPERTY(unsigned verbose READ verbose WRITE setVerbose)
+    Q_OBJECT
+public:
+    enum MessageType { MessageWithReply,
+                       MessageWithoutReply, /* Non-standard: "Settings:set" command does not reply */
+                       NoopMessage };
+
+    typedef QSharedPointer<QIODevice> IODevicePtr;
+
+    explicit TcfTrkDevice(QObject *parent = 0);
+    virtual ~TcfTrkDevice();
+
+    unsigned verbose() const;
+
+    // Mapping of register names for indices
+    QVector<QByteArray> registerNames() const;
+    void setRegisterNames(const QVector<QByteArray>& n);
+
+    IODevicePtr device() const;
+    IODevicePtr takeDevice();
+    void setDevice(const IODevicePtr &dp);
+
+    void sendTcfTrkMessage(MessageType mt, Services service,
+                           const char *command,
+                           const char *commandParameters, int commandParametersLength,
+                           const TcfTrkCallback &callBack = TcfTrkCallback(),
+                           const QVariant &cookie = QVariant());
+
+    void sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+                           const QByteArray &commandParameters,
+                           const TcfTrkCallback &callBack = TcfTrkCallback(),
+                           const QVariant &cookie = QVariant());
+
+    // Convenience messages: Start a process
+    void sendProcessStartCommand(const TcfTrkCallback &callBack,
+                                 const QString &binary,
+                                 unsigned uid,
+                                 QStringList arguments = QStringList(),
+                                 QString workingDirectory = QString(),
+                                 bool debugControl = true,
+                                 const QStringList &additionalLibraries = QStringList(),
+                                 const QVariant &cookie = QVariant());
+
+    void sendProcessTerminateCommand(const TcfTrkCallback &callBack,
+                                     const QByteArray &id,
+                                     const QVariant &cookie = QVariant());
+
+    void sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
+                                      const QByteArray &id,
+                                      const QVariant &cookie = QVariant());
+
+    // Resume / Step (see RunControlResumeMode).
+    void sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+                                     const QByteArray &id,
+                                     RunControlResumeMode mode,
+                                     unsigned count /* = 1, currently ignored. */,
+                                     quint64 rangeStart, quint64 rangeEnd,
+                                     const QVariant &cookie = QVariant());
+
+    // Convenience to resume a suspended process
+    void sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+                                     const QByteArray &id,
+                                     const QVariant &cookie = QVariant());
+
+    void sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
+                                   const Breakpoint &b,
+                                   const QVariant &cookie = QVariant());
+
+    void sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+                                      const QByteArray &id,
+                                      const QVariant &cookie = QVariant());
+
+    void sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+                                      const QVector<QByteArray> &id,
+                                      const QVariant &cookie = QVariant());
+
+    void sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+                                      const QByteArray &id,
+                                      bool enable,
+                                      const QVariant &cookie = QVariant());
+
+    void sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+                                      const QVector<QByteArray> &id,
+                                      bool enable,
+                                      const QVariant &cookie = QVariant());
+
+
+    void sendMemoryGetCommand(const TcfTrkCallback &callBack,
+                              const QByteArray &contextId,
+                              quint64 start, quint64 size,
+                              const QVariant &cookie = QVariant());
+
+    void sendMemorySetCommand(const TcfTrkCallback &callBack,
+                              const QByteArray &contextId,
+                              quint64 start, const QByteArray& data,
+                              const QVariant &cookie = QVariant());
+
+    // Reply is an array of hexvalues
+    void sendRegistersGetMCommand(const TcfTrkCallback &callBack,
+                                  const QByteArray &contextId,
+                                  const QVector<QByteArray> &ids,
+                                  const QVariant &cookie = QVariant());
+
+    // Convenience to get a range of register "R0" .. "R<n>".
+    // Cookie will be an int containing "start".
+    void sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
+                                 const QByteArray &contextId,
+                                  unsigned start, unsigned count);
+
+    // Set register
+    void sendRegistersSetCommand(const TcfTrkCallback &callBack,
+                                 const QByteArray &contextId,
+                                 const QByteArray &ids,
+                                 unsigned value,
+                                 const QVariant &cookie = QVariant());
+    // Set register
+    void sendRegistersSetCommand(const TcfTrkCallback &callBack,
+                                 const QByteArray &contextId,
+                                 unsigned registerNumber,
+                                 unsigned value,
+                                 const QVariant &cookie = QVariant());
+
+    static QByteArray parseMemoryGet(const TcfTrkCommandResult &r);
+
+signals:
+    void genericTcfEvent(int service, const QByteArray &name, const QVector<tcftrk::JsonValue> &value);
+    void tcfEvent(const tcftrk::TcfTrkEvent &knownEvent);
+
+    void logMessage(const QString &);
+    void error(const QString &);
+
+public slots:
+    void setVerbose(unsigned v);
+
+private slots:
+    void slotDeviceError();
+    void slotDeviceSocketStateChanged();
+    void slotDeviceReadyRead();
+
+private:
+    bool checkOpen();
+    void checkSendQueue();
+    void writeMessage(QByteArray data);
+    void emitLogMessage(const QString &);
+    int parseMessage(const QByteArray &);
+    int parseTcfCommandReply(char type, const QVector<QByteArray> &tokens);
+    int parseTcfEvent(const QVector<QByteArray> &tokens);
+
+    TcfTrkDevicePrivate *d;
+};
+
+} // namespace tcftrk
+
+#endif // TCFTRKENGINE_H
diff --git a/src/shared/symbianutils/tcftrkmessage.cpp b/src/shared/symbianutils/tcftrkmessage.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..929bb8d05af5fecc74d4ea2283d0fa8015043c0e
--- /dev/null
+++ b/src/shared/symbianutils/tcftrkmessage.cpp
@@ -0,0 +1,550 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 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 "tcftrkmessage.h"
+#include "json.h"
+
+#include <QtCore/QString>
+#include <QtCore/QTextStream>
+
+// Names matching the enum
+static const char *serviceNamesC[] =
+{ "Locator", "RunControl", "Processes", "Memory", "Settings", "Breakpoints",
+  "Registers", "SimpleRegisters",
+  "UnknownService"};
+
+namespace tcftrk {
+
+SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep)
+{
+    QString rc;
+    const int count = a.size();
+    for (int i = 0; i < count; i++) {
+        if (i)
+            rc += QLatin1Char(sep);
+        rc += QString::fromUtf8(a.at(i));
+    }
+    return rc;
+}
+
+static inline bool jsonToBool(const JsonValue& js)
+{
+    return js.type() == JsonValue::Boolean && js.data() == "true";
+}
+
+SYMBIANUTILS_EXPORT const char *serviceName(Services s)
+{
+    return serviceNamesC[s];
+}
+
+SYMBIANUTILS_EXPORT Services serviceFromName(const char *n)
+{
+    const int count = sizeof(serviceNamesC)/sizeof(char *);
+    for (int i = 0; i < count; i++)
+        if (!qstrcmp(serviceNamesC[i], n))
+            return static_cast<Services>(i);
+    return UnknownService;
+}
+
+SYMBIANUTILS_EXPORT QString formatData(const QByteArray &a)
+{
+    const int columns = 16;
+    QString rc;
+    QTextStream str(&rc);
+    str.setIntegerBase(16);
+    str.setPadChar(QLatin1Char('0'));
+    const unsigned char *start = reinterpret_cast<const unsigned char *>(a.constData());
+    const unsigned char *end = start + a.size();
+    for  (const unsigned char *p = start; p < end ; ) {
+        str << "0x";
+        str.setFieldWidth(4);
+        str << (p - start);
+        str.setFieldWidth(0);
+        str << ' ';
+        QString asc;
+        int c = 0;
+        for ( ; c < columns && p < end; c++, p++) {
+            const unsigned u = *p;
+            str.setFieldWidth(2);
+            str << u;
+            str.setFieldWidth(0);
+            str << ' ';
+            switch (u) {
+            case '\n':
+                asc += QLatin1String("\\n");
+                break;
+            case '\r':
+                asc += QLatin1String("\\r");
+                break;
+            case '\t':
+                asc += QLatin1String("\\t");
+                break;
+            default:
+                if (u >= 32 && u < 128) {
+                    asc += QLatin1Char(' ');
+                    asc += QLatin1Char(u);
+                } else {
+                    asc += QLatin1String(" .");
+                }
+                break;
+            }
+        }
+        if (const int remainder = columns - c)
+            str << QString(3 * remainder, QLatin1Char(' '));
+        str << ' ' << asc << '\n';
+    }
+    return rc;
+}
+
+// ----------- RunControlContext
+RunControlContext::RunControlContext() :
+        flags(0), resumeFlags(0)
+{
+}
+
+void RunControlContext::clear()
+{
+    flags =0;
+    resumeFlags = 0;
+    id.clear();
+    osid.clear();
+    parentId.clear();
+}
+
+RunControlContext::Type RunControlContext::typeFromTcfId(const QByteArray &id)
+{
+    // "p12" or "p12.t34"?
+    return id.contains(".t") ? Thread : Process;
+}
+
+unsigned RunControlContext::processId() const
+{
+    return processIdFromTcdfId(id);
+}
+
+unsigned RunControlContext::threadId() const
+{
+    return threadIdFromTcdfId(id);
+}
+
+unsigned RunControlContext::processIdFromTcdfId(const QByteArray &id)
+{
+    // Cut out process id from "p12" or "p12.t34"?
+    if (!id.startsWith('p'))
+        return 0;
+    const int dotPos = id.indexOf('.');
+    const int pLen = dotPos == -1 ? id.size() : dotPos;
+    return id.mid(1, pLen - 1).toUInt();
+}
+
+unsigned RunControlContext::threadIdFromTcdfId(const QByteArray &id)
+{
+    const int tPos = id.indexOf(".t");
+    return tPos != -1 ? id.mid(tPos + 2).toUInt() : uint(0);
+}
+
+QByteArray RunControlContext::tcfId(unsigned processId,  unsigned threadId /* = 0 */)
+{
+    QByteArray rc("p");
+    rc += QByteArray::number(processId);
+    if (threadId) {
+        rc += ".t";
+        rc += QByteArray::number(threadId);
+    }
+    return rc;
+}
+
+RunControlContext::Type RunControlContext::type() const
+{
+    return RunControlContext::typeFromTcfId(id);
+}
+
+bool RunControlContext::parse(const JsonValue &val)
+{
+    clear();
+    if (val.type() != JsonValue::Object)
+        return false;
+    foreach(const JsonValue &c, val.children()) {
+        if (c.name() == "ID") {
+            id = c.data();
+        } else if (c.name() == "OSID") {
+            osid = c.data();
+        } else if (c.name() == "ParentID") {
+            parentId = c.data();
+        }  else if (c.name() == "IsContainer") {
+            if (jsonToBool(c))
+                flags |= Container;
+        }  else if (c.name() == "CanTerminate") {
+            if (jsonToBool(c))
+                flags |= CanTerminate;
+        }  else if (c.name() == "CanResume") {
+            resumeFlags = c.data().toUInt();
+        }  else if (c.name() == "HasState") {
+            if (jsonToBool(c))
+                flags |= HasState;
+        } else if (c.name() == "CanSuspend") {
+            if (jsonToBool(c))
+                flags |= CanSuspend;
+        }
+    }
+    return true;
+}
+
+QString RunControlContext::toString() const
+{
+    QString rc;
+    QTextStream str(&rc);
+    format(str);
+    return rc;
+}
+
+void RunControlContext::format(QTextStream &str) const
+{
+    str << " id='" << id << "' osid='" << osid
+        << "' parentId='" << parentId <<"' ";
+    if (flags & Container)
+        str << "[container] ";
+    if (flags & HasState)
+        str << "[has state] ";
+    if (flags & CanSuspend)
+        str << "[can suspend] ";
+    if (flags & CanSuspend)
+        str << "[can terminate] ";
+    str.setIntegerBase(16);
+    str << " resume_flags: 0x" <<  resumeFlags;
+    str.setIntegerBase(10);
+}
+
+// ------ ModuleLoadEventInfo
+ModuleLoadEventInfo::ModuleLoadEventInfo() :
+   loaded(false), codeAddress(0), dataAddress(0), requireResume(false)
+{
+}
+
+void ModuleLoadEventInfo::clear()
+{
+    loaded = requireResume = false;
+    codeAddress = dataAddress =0;
+}
+
+bool ModuleLoadEventInfo::parse(const JsonValue &val)
+{
+    clear();
+    if (val.type() != JsonValue::Object)
+        return false;
+    foreach(const JsonValue &c, val.children()) {
+        if (c.name() == "Name") {
+            name = c.data();
+        } else if (c.name() == "File") {
+            file = c.data();
+        } else if (c.name() == "CodeAddress") {
+            codeAddress = c.data().toULongLong();
+        }  else if (c.name() == "DataAddress") {
+            dataAddress = c.data().toULongLong();
+        }  else if (c.name() == "Loaded") {
+            loaded = jsonToBool(c);
+        }  else if (c.name() == "RequireResume") {
+            requireResume =jsonToBool(c);
+        }
+    }
+    return true;
+}
+void ModuleLoadEventInfo::format(QTextStream &str) const
+{
+    str << "name='" << name << "' file='" << file << "' " <<
+            (loaded ? "[loaded] " : "[not loaded] ");
+    if (requireResume)
+        str << "[requires resume] ";
+    str.setIntegerBase(16);
+    str  << " code: 0x" << codeAddress << " data: 0x" << dataAddress;
+    str.setIntegerBase(10);
+}
+
+// ---------------------- Breakpoint
+
+// Types matching enum
+static const char *breakPointTypesC[] = {"Software", "Hardware", "Auto"};
+
+Breakpoint::Breakpoint(quint64 loc) :
+    type(Auto), enabled(true), ignoreCount(0), location(loc), size(1), thumb(true)
+{
+    if (loc)
+        id = idFromLocation(location);
+}
+
+void Breakpoint::setContextId(unsigned processId, unsigned threadId)
+{
+    contextIds = QVector<QByteArray>(1, RunControlContext::tcfId(processId, threadId));
+}
+
+QByteArray Breakpoint::idFromLocation(quint64 loc)
+{
+    return QByteArray("BP_0x") + QByteArray::number(loc, 16);
+}
+
+QString Breakpoint::toString() const
+{
+    QString rc;
+    QTextStream str(&rc);
+    str.setIntegerBase(16);
+    str << "Breakpoint '" << id << "' "  << breakPointTypesC[type] << " for contexts '"
+            << joinByteArrays(contextIds, ',') << "' at 0x" << location;
+    str.setIntegerBase(10);
+    str << " size " << size;
+    if (enabled)
+        str << " [enabled]";
+    if (thumb)
+        str << " [thumb]";
+    if (ignoreCount)
+        str << " IgnoreCount " << ignoreCount;
+    return rc;
+}
+
+JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b)
+{
+    if (b.contextIds.isEmpty())
+        qWarning("tcftrk::Breakpoint: No context ids specified");
+
+    str << '{' << "ID" << ':' << QString::fromUtf8(b.id) << ','
+        << "BreakpointType" << ':' << breakPointTypesC[b.type] << ','
+        << "Enabled" << ':' << b.enabled << ','
+        << "IgnoreCount" << ':' << b.ignoreCount << ','
+        << "ContextIds" << ':' << b.contextIds << ','
+        << "Location" << ':' << QString::number(b.location) << ','
+        << "Size"  << ':' << b.size << ','
+        << "THUMB_BREAKPOINT" << ':' << b.thumb
+        << '}';
+    return str;
+}
+
+// --- Events
+TcfTrkEvent::TcfTrkEvent(Type type) : m_type(type)
+{
+}
+
+TcfTrkEvent::~TcfTrkEvent()
+{
+}
+
+TcfTrkEvent::Type TcfTrkEvent::type() const
+{
+    return m_type;
+}
+
+QString TcfTrkEvent::toString() const
+{
+    return QString();
+}
+
+static const char sharedLibrarySuspendReasonC[] = "Shared Library";
+
+TcfTrkEvent *TcfTrkEvent::parseEvent(Services s, const QByteArray &nameBA, const QVector<JsonValue> &values)
+{
+    switch (s) {
+    case LocatorService:
+        if (nameBA == "Hello" && values.size() == 1 && values.front().type() == JsonValue::Array) {
+            QStringList services;
+            foreach (const JsonValue &jv, values.front().children())
+                services.push_back(QString::fromUtf8(jv.data()));
+            return new TcfTrkLocatorHelloEvent(services);
+        }
+        break;
+    case RunControlService:
+        if (values.empty())
+            return 0;
+        // "id/PC/Reason/Data"
+        if (nameBA == "contextSuspended" && values.size() == 4) {
+            const QByteArray idBA = values.at(0).data();
+            const quint64 pc = values.at(1).data().toULongLong();
+            const QByteArray reasonBA = values.at(2).data();
+            // Module load: Special
+            if (reasonBA == sharedLibrarySuspendReasonC) {
+                ModuleLoadEventInfo info;
+                if (!info.parse(values.at(3)))
+                    return 0;
+                return new TcfTrkRunControlModuleLoadContextSuspendedEvent(idBA, reasonBA, pc, info);
+            }
+            return new TcfTrkRunControlContextSuspendedEvent(idBA, reasonBA, pc);
+        } // "contextSuspended"
+        if (nameBA == "contextAdded")
+            return TcfTrkRunControlContextAddedEvent::parseEvent(values);
+        if (nameBA == "contextRemoved" && values.front().type() == JsonValue::Array) {
+            QVector<QByteArray> ids;
+            foreach(const JsonValue &c, values.front().children())
+                ids.push_back(c.data());
+            return new TcfTrkRunControlContextRemovedEvent(ids);
+        }
+        break;
+   default:
+        break;
+    }
+    return 0;
+}
+
+// -------------- TcfTrkServiceHelloEvent
+TcfTrkLocatorHelloEvent::TcfTrkLocatorHelloEvent(const QStringList &s) :
+    TcfTrkEvent(LocatorHello),
+    m_services(s)
+{
+}
+
+QString TcfTrkLocatorHelloEvent::toString() const
+{
+    return QLatin1String("ServiceHello: ") + m_services.join(QLatin1String(", "));
+}
+
+// -------------- TcfTrkIdEvent
+TcfTrkIdEvent::TcfTrkIdEvent(Type t, const QByteArray &id) :
+   TcfTrkEvent(t), m_id(id)
+{
+}
+
+// ---------- TcfTrkIdsEvent
+TcfTrkIdsEvent::TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids) :
+    TcfTrkEvent(t), m_ids(ids)
+{
+}
+
+QString TcfTrkIdsEvent::joinedIdString(const char sep) const
+{
+    return joinByteArrays(m_ids, sep);
+}
+
+//  ---------------- TcfTrkRunControlContextAddedEvent
+TcfTrkRunControlContextAddedEvent::TcfTrkRunControlContextAddedEvent(const RunControlContexts &c) :
+        TcfTrkEvent(RunControlContextAdded), m_contexts(c)
+{
+}
+
+TcfTrkRunControlContextAddedEvent
+        *TcfTrkRunControlContextAddedEvent::parseEvent(const QVector<JsonValue> &values)
+{
+    // Parse array of contexts
+    if (values.size() < 1 || values.front().type() != JsonValue::Array)
+        return 0;
+
+    RunControlContexts contexts;
+    foreach (const JsonValue &v, values.front().children()) {
+        RunControlContext context;
+        if (context.parse(v))
+            contexts.push_back(context);
+    }
+    return new TcfTrkRunControlContextAddedEvent(contexts);
+}
+
+QString TcfTrkRunControlContextAddedEvent::toString() const
+{
+    QString rc;
+    QTextStream str(&rc);
+    str << "RunControl: " << m_contexts.size() << " context(s) "
+        << (type() == RunControlContextAdded ? "added" : "removed")
+        << '\n';
+    foreach (const RunControlContext &c, m_contexts) {
+        c.format(str);
+        str << '\n';
+    }
+    return rc;
+}
+
+// --------------- TcfTrkRunControlContextRemovedEvent
+TcfTrkRunControlContextRemovedEvent::TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &ids) :
+        TcfTrkIdsEvent(RunControlContextRemoved, ids)
+{
+}
+
+QString TcfTrkRunControlContextRemovedEvent::toString() const
+{
+    return QLatin1String("RunControl: Removed contexts '") + joinedIdString() + ("'.");
+}
+
+// --------------- TcfTrkRunControlContextSuspendedEvent
+TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+                                                                             const QByteArray &reason,
+                                                                             quint64 pc) :
+        TcfTrkIdEvent(RunControlSuspended, id), m_pc(pc), m_reason(reason)
+{
+}
+
+TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(Type t,
+                                                                             const QByteArray &id,
+                                                                             const QByteArray &reason,
+                                                                             quint64 pc) :
+        TcfTrkIdEvent(t, id), m_pc(pc), m_reason(reason)
+{
+}
+
+void TcfTrkRunControlContextSuspendedEvent::format(QTextStream &str) const
+{
+    str.setIntegerBase(16);
+    str << "RunControl: '" << idString()  << "' suspended at 0x"
+            << m_pc << ": '" << m_reason << "'.";
+    str.setIntegerBase(10);
+}
+
+QString TcfTrkRunControlContextSuspendedEvent::toString() const
+{
+    QString rc;
+    QTextStream str(&rc);
+    format(str);
+    return rc;
+}
+
+TcfTrkRunControlContextSuspendedEvent::Reason TcfTrkRunControlContextSuspendedEvent::reason() const
+{
+    if (m_reason == sharedLibrarySuspendReasonC)
+        return ModuleLoad;
+    if (m_reason == "Breakpoint")
+        return BreakPoint;
+    // 'Data abort exception'/'Thread has panicked' ... unfortunately somewhat unspecific.
+    if (m_reason.contains("exception") || m_reason.contains("panick"))
+        return Crash;
+    return Other;
+}
+
+TcfTrkRunControlModuleLoadContextSuspendedEvent::TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+                                                                                                 const QByteArray &reason,
+                                                                                                 quint64 pc,
+                                                                                                 const ModuleLoadEventInfo &mi) :
+    TcfTrkRunControlContextSuspendedEvent(RunControlModuleLoadSuspended, id, reason, pc),
+    m_mi(mi)
+{
+}
+
+QString TcfTrkRunControlModuleLoadContextSuspendedEvent::toString() const
+{
+    QString rc;
+    QTextStream str(&rc);
+    TcfTrkRunControlContextSuspendedEvent::format(str);
+    str <<  ' ';
+    m_mi.format(str);
+    return rc;
+}
+
+
+} // namespace tcftrk
diff --git a/src/shared/symbianutils/tcftrkmessage.h b/src/shared/symbianutils/tcftrkmessage.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b073c8ab406c325dca0d4fdc43b5dee0b3d9f17
--- /dev/null
+++ b/src/shared/symbianutils/tcftrkmessage.h
@@ -0,0 +1,284 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 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 TRCFTRKMESSAGE_H
+#define TRCFTRKMESSAGE_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+QT_BEGIN_NAMESPACE
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace tcftrk {
+
+class JsonValue;
+class JsonInputStream;
+
+enum Services {
+    LocatorService,
+    RunControlService,
+    ProcessesService,
+    MemoryService,
+    SettingsService,  // non-standard, trk specific
+    BreakpointsService,
+    RegistersService,
+    SimpleRegistersService, // non-standard, trk specific
+    UnknownService
+}; // Note: Check string array 'serviceNamesC' of same size when modifying this.
+
+// Modes of RunControl/'Resume' (see EDF documentation).
+// As of 24.6.2010, RM_RESUME, RM_STEP_OVER, RM_STEP_INTO,
+// RM_STEP_OVER_RANGE, RM_STEP_INTO_RANGE are supported with
+// RANG_START/RANGE_END parameters.
+enum RunControlResumeMode {
+    RM_RESUME = 0,
+    RM_STEP_OVER = 1, RM_STEP_INTO = 2,
+    RM_STEP_OVER_LINE = 3, RM_STEP_INTO_LINE = 4,
+    RM_STEP_OUT = 5, RM_REVERSE_RESUME = 6,
+    RM_REVERSE_STEP_OVER = 7, RM_REVERSE_STEP_INTO = 8,
+    RM_REVERSE_STEP_OVER_LINE = 9, RM_REVERSE_STEP_INTO_LINE = 10,
+    RM_REVERSE_STEP_OUT = 11, RM_STEP_OVER_RANGE = 12,
+    RM_STEP_INTO_RANGE = 13, RM_REVERSE_STEP_OVER_RANGE = 14,
+    RM_REVERSE_STEP_INTO_RANGE = 15
+};
+
+SYMBIANUTILS_EXPORT const char *serviceName(Services s);
+SYMBIANUTILS_EXPORT Services serviceFromName(const char *);
+
+// Debug helpers
+SYMBIANUTILS_EXPORT QString formatData(const QByteArray &a);
+SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep = ',');
+
+// Context used in 'RunControl contextAdded' events and in reply
+// to 'Processes start'. Could be thread or process.
+struct SYMBIANUTILS_EXPORT RunControlContext {
+    enum Flags {
+        Container = 0x1, HasState = 0x2, CanSuspend = 0x4,
+        CanTerminate = 0x8
+    };
+    enum Type { Process, Thread };
+
+    RunControlContext();
+    Type type() const;
+    unsigned processId() const;
+    unsigned threadId() const;
+
+    void clear();
+    bool parse(const JsonValue &v);
+    void format(QTextStream &str) const;
+    QString toString() const;
+
+    // Helper for converting the TCF ids ("p12" or "p12.t34")
+    static Type typeFromTcfId(const QByteArray &id);
+    static unsigned processIdFromTcdfId(const QByteArray &id);
+    static unsigned threadIdFromTcdfId(const QByteArray &id);
+    static QByteArray tcfId(unsigned processId,  unsigned threadId = 0);
+
+    unsigned flags;
+    unsigned resumeFlags;
+    QByteArray id;     // "p434.t699"
+    QByteArray osid;   // Non-standard: Process or thread id
+    QByteArray parentId; // Parent process id of a thread.
+};
+
+// Module load information occuring with 'RunControl contextSuspended' events
+struct SYMBIANUTILS_EXPORT ModuleLoadEventInfo {
+    ModuleLoadEventInfo();
+    void clear();
+    bool parse(const JsonValue &v);
+    void format(QTextStream &str) const;
+
+    QByteArray name;
+    QByteArray file;
+    bool loaded;
+    quint64 codeAddress;
+    quint64 dataAddress;
+    bool requireResume;
+};
+
+// Breakpoint as supported by TcfTrk source June 2010
+// TODO: Add watchpoints,etc once they are implemented
+struct SYMBIANUTILS_EXPORT Breakpoint {
+    enum Type { Software, Hardware, Auto };
+
+    explicit Breakpoint(quint64 loc = 0);
+    void setContextId(unsigned processId, unsigned threadId = 0);
+    QString toString() const;
+
+    static QByteArray idFromLocation(quint64 loc); // Automagically determine from location
+
+    Type type;
+    bool enabled;
+    int ignoreCount;
+    QVector<QByteArray> contextIds;   // Process or thread ids.
+    QByteArray id;                    // Id of the breakpoint;
+    quint64 location;
+    unsigned size;
+    bool thumb;
+};
+
+SYMBIANUTILS_EXPORT JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b);
+
+// Event hierarchy
+class SYMBIANUTILS_EXPORT TcfTrkEvent {
+    Q_DISABLE_COPY(TcfTrkEvent)
+public:
+    enum Type { None,
+                LocatorHello,
+                RunControlContextAdded,
+                RunControlContextRemoved,
+                RunControlSuspended,
+                RunControlBreakpointSuspended,
+                RunControlModuleLoadSuspended,
+                RunControlResumed
+              };
+
+    virtual ~TcfTrkEvent();
+
+    Type type() const;
+    virtual QString toString() const;
+
+    static TcfTrkEvent *parseEvent(Services s, const QByteArray &name, const QVector<JsonValue> &val);
+
+protected:
+    explicit TcfTrkEvent(Type type = None);
+
+private:
+    const Type m_type;
+};
+
+// ServiceHello
+class SYMBIANUTILS_EXPORT TcfTrkLocatorHelloEvent : public TcfTrkEvent {
+public:
+    explicit TcfTrkLocatorHelloEvent(const QStringList &);
+
+    const QStringList &services() { return m_services; }
+    virtual QString toString() const;
+
+private:
+    QStringList m_services;
+};
+
+// Base for events that just have one id as parameter
+// (simple suspend)
+class SYMBIANUTILS_EXPORT TcfTrkIdEvent : public TcfTrkEvent {
+protected:
+    explicit TcfTrkIdEvent(Type t, const QByteArray &id);
+public:
+    QByteArray id() const { return m_id; }
+    QString idString() const { return QString::fromUtf8(m_id); }
+
+private:
+    const QByteArray m_id;
+};
+
+// Base for events that just have some ids as parameter
+// (context removed)
+class SYMBIANUTILS_EXPORT TcfTrkIdsEvent : public TcfTrkEvent {
+protected:
+    explicit TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids);
+
+public:
+    QVector<QByteArray> ids() const { return m_ids; }
+    QString joinedIdString(const char sep = ',') const;
+
+private:
+    const QVector<QByteArray> m_ids;
+};
+
+// RunControlContextAdded
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextAddedEvent : public TcfTrkEvent {
+public:
+    typedef QVector<RunControlContext> RunControlContexts;
+
+    explicit TcfTrkRunControlContextAddedEvent(const RunControlContexts &c);
+
+    const RunControlContexts &contexts() const { return m_contexts; }
+    virtual QString toString() const;
+
+    static TcfTrkRunControlContextAddedEvent *parseEvent(const QVector<JsonValue> &val);
+
+private:
+    const RunControlContexts m_contexts;
+};
+
+// RunControlContextRemoved
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextRemovedEvent : public TcfTrkIdsEvent {
+public:
+    explicit TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &id);
+    virtual QString toString() const;
+};
+
+// Simple RunControlContextSuspended (process/thread)
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextSuspendedEvent : public TcfTrkIdEvent {
+public:
+    enum Reason  { BreakPoint, ModuleLoad, Crash, Other } ;
+
+    explicit TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+                                                   const QByteArray &reason,
+                                                   quint64 pc = 0);
+    virtual QString toString() const;
+
+    quint64 pc() const { return m_pc; }
+    QByteArray reasonID() const { return m_reason; }
+    Reason reason() const;
+
+protected:
+    explicit TcfTrkRunControlContextSuspendedEvent(Type t,
+                                                   const QByteArray &id,
+                                                   const QByteArray &reason,
+                                                   quint64 pc = 0);
+    void format(QTextStream &str) const;
+
+private:
+    const quint64 m_pc;
+    const QByteArray m_reason;
+};
+
+// RunControlContextSuspended due to module load
+class SYMBIANUTILS_EXPORT TcfTrkRunControlModuleLoadContextSuspendedEvent : public TcfTrkRunControlContextSuspendedEvent {
+public:
+    explicit TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+                                                             const QByteArray &reason,
+                                                             quint64 pc,
+                                                             const ModuleLoadEventInfo &mi);
+
+    virtual QString toString() const;
+    const ModuleLoadEventInfo &info() const { return m_mi; }
+
+private:
+    const ModuleLoadEventInfo m_mi;
+};
+
+} // namespace tcftrk
+#endif // TRCFTRKMESSAGE_H
diff --git a/src/shared/symbianutils/trkutils.cpp b/src/shared/symbianutils/trkutils.cpp
index fd9978c736ff4364d92a00e6fa1825bd07a093e7..8b40d1f88d4663ae5607ef502b246e7dd11db64e 100644
--- a/src/shared/symbianutils/trkutils.cpp
+++ b/src/shared/symbianutils/trkutils.cpp
@@ -131,6 +131,49 @@ QString Session::deviceDescription(unsigned verbose) const
     return msg.arg(formatTrkVersion(trkAppVersion));
 }
 
+QString Session::toString() const
+{
+    QString rc;
+    QTextStream str(&rc);
+    str << "Session: " << deviceDescription(false) << '\n'
+            << "pid: " << pid <<  " thread: " << tid << ' ';
+    str.setIntegerBase(16);
+    str << " code: 0x" << codeseg << " data: 0x" << dataseg << '\n';
+    if (const int libCount = libraries.size()) {
+        str << "Libraries:\n";
+        for (int i = 0; i < libCount; i++)
+            str << " #" << i << ' ' << libraries.at(i).name
+                << " code: 0x" << libraries.at(i).codeseg
+                << " data: 0x" << libraries.at(i).dataseg << '\n';
+    }
+    if (const int moduleCount = modules.size()) {
+        str << "Modules:\n";
+        for (int i = 0; i < moduleCount; i++)
+            str << " #" << i << ' ' << modules.at(i) << '\n';
+    }
+    str.setIntegerBase(10);
+    str <<  "Current thread: " << currentThread << '\n';
+    if (const int threadCount = threads.size()) {
+        str << "Threads:\n";
+        for (int i = 0; i < threadCount; i++)
+            str << " #" << i << ' ' << threads.at(i);
+    }
+
+    if (!addressToBP.isEmpty()) {
+        typedef QHash<uint, uint>::const_iterator BP_ConstIterator;
+        str << "Breakpoints:\n";
+        const BP_ConstIterator cend = addressToBP.constEnd();
+        for (BP_ConstIterator it = addressToBP.constBegin(); it != cend; ++it) {
+            str.setIntegerBase(16);
+            str << "  0x" << it.key();
+            str.setIntegerBase(10);
+            str << ' ' << it.value() << '\n';
+        }
+    }
+
+    return rc;
+}
+
 // --------------
 
 QByteArray decode7d(const QByteArray &ba)
diff --git a/src/shared/symbianutils/trkutils.h b/src/shared/symbianutils/trkutils.h
index e438c1b0ceae4cd41175fe386ac46f650d112e3d..2cf2c99da4b761d08c9a3c43062fe8ee0bc5d0a9 100644
--- a/src/shared/symbianutils/trkutils.h
+++ b/src/shared/symbianutils/trkutils.h
@@ -175,6 +175,7 @@ struct SYMBIANUTILS_EXPORT Session
     Session();
     void reset();
     QString deviceDescription(unsigned verbose) const;
+    QString toString() const;
 
     // Trk feedback
     byte cpuMajor;