From beba423a16fd0e6e836cd8c26ca30e5167ca56db Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Fri, 25 Jun 2010 15:06:30 +0200
Subject: [PATCH] Debugger[Trk]: Added TCF experimental adapter.

Added adapter to work with TCF over WLAN.
Factor out common code for Symbian from TrkAdapter.
Improve message logging in TrkGdbAdapter, some cleanup.
Added new TcfTrkGdbAdapter based on TcfTrkDevice
in symbianutils based on JSON, using QTcpSocket.
To be started via special @tcf@ argument.
Rubber-stamped-by: hjk
---
 src/plugins/debugger/debuggerdialogs.cpp      |    2 -
 src/plugins/debugger/debuggerplugin.cpp       |    4 +
 src/plugins/debugger/debuggerrunner.cpp       |    4 +
 src/plugins/debugger/gdb/gdb.pri              |    8 +-
 src/plugins/debugger/gdb/gdbengine.cpp        |    8 +-
 src/plugins/debugger/gdb/gdbengine.h          |    1 +
 src/plugins/debugger/gdb/symbian.cpp          |  215 +++
 src/plugins/debugger/gdb/symbian.h            |  145 ++
 src/plugins/debugger/gdb/tcftrkgdbadapter.cpp | 1522 +++++++++++++++++
 src/plugins/debugger/gdb/tcftrkgdbadapter.h   |  186 ++
 src/plugins/debugger/gdb/trkgdbadapter.cpp    |  265 +--
 src/plugins/debugger/gdb/trkgdbadapter.h      |  120 +-
 src/shared/symbianutils/json.cpp              |  478 ++++++
 src/shared/symbianutils/json.h                |  137 ++
 src/shared/symbianutils/symbianutils.pri      |   12 +-
 src/shared/symbianutils/tcftrkdevice.cpp      |  889 ++++++++++
 src/shared/symbianutils/tcftrkdevice.h        |  271 +++
 src/shared/symbianutils/tcftrkmessage.cpp     |  550 ++++++
 src/shared/symbianutils/tcftrkmessage.h       |  284 +++
 src/shared/symbianutils/trkutils.cpp          |   43 +
 src/shared/symbianutils/trkutils.h            |    1 +
 21 files changed, 4830 insertions(+), 315 deletions(-)
 create mode 100644 src/plugins/debugger/gdb/symbian.cpp
 create mode 100644 src/plugins/debugger/gdb/symbian.h
 create mode 100644 src/plugins/debugger/gdb/tcftrkgdbadapter.cpp
 create mode 100644 src/plugins/debugger/gdb/tcftrkgdbadapter.h
 create mode 100644 src/shared/symbianutils/json.cpp
 create mode 100644 src/shared/symbianutils/json.h
 create mode 100644 src/shared/symbianutils/tcftrkdevice.cpp
 create mode 100644 src/shared/symbianutils/tcftrkdevice.h
 create mode 100644 src/shared/symbianutils/tcftrkmessage.cpp
 create mode 100644 src/shared/symbianutils/tcftrkmessage.h

diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp
index 49705dc13a7..3795f73688a 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 16483b8dfc9..ce67ada9169 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 9c38c145008..d642beeb840 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 ffe58619521..e94b175bdf3 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 c8a0ad106d7..5605cb8088f 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 4dd23a004ad..6a45096237b 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 00000000000..9388fdf7338
--- /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 00000000000..63e3c4e48f1
--- /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 00000000000..e6ab8354933
--- /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 00000000000..0953700cdb8
--- /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 b417d688a08..98f36a8be61 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 c2af38308ae..b24c8cd1f96 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 00000000000..4171125ee97
--- /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 00000000000..ef574bdae24
--- /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 6309517b183..f07e494ed17 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 00000000000..9f646e3ec83
--- /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 00000000000..be5077b2a08
--- /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 00000000000..929bb8d05af
--- /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 00000000000..8b073c8ab40
--- /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 fd9978c736f..8b40d1f88d4 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 e438c1b0cea..2cf2c99da4b 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;
-- 
GitLab