From f34fd810d5a1f48c935e459e42e273f0e6102558 Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Wed, 11 Aug 2010 15:46:55 +0200
Subject: [PATCH] Debugger[TCF TRK]: Adapt to TCF TRK 4.0.5

- Parse/Format errors correctly (long codes)
- Use standard 'Registers' service, fake 'getm' as long as it is
unimplemented
- Use 'Registers|getChildren' for each thread to activate the context
and use the names obtained from there instead of hardcoded register
names
---
 src/plugins/debugger/gdb/symbian.cpp          |  20 +-
 src/plugins/debugger/gdb/symbian.h            |   1 +
 src/plugins/debugger/gdb/tcftrkgdbadapter.cpp | 126 ++++++++---
 src/plugins/debugger/gdb/tcftrkgdbadapter.h   |   2 +
 src/shared/symbianutils/tcftrkdevice.cpp      | 198 +++++++++++++++---
 src/shared/symbianutils/tcftrkdevice.h        |  41 +++-
 src/shared/symbianutils/tcftrkmessage.cpp     |   2 +-
 src/shared/symbianutils/tcftrkmessage.h       |   1 -
 8 files changed, 317 insertions(+), 74 deletions(-)

diff --git a/src/plugins/debugger/gdb/symbian.cpp b/src/plugins/debugger/gdb/symbian.cpp
index 46ad97537c1..34e87921753 100644
--- a/src/plugins/debugger/gdb/symbian.cpp
+++ b/src/plugins/debugger/gdb/symbian.cpp
@@ -147,17 +147,23 @@ QByteArray Thread::gdbReportRegisters() const
     return ba;
 }
 
+QByteArray Thread::registerContentsLogMessage() const
+{
+    QByteArray logMsg;
+    for (int i = 0; i < RegisterCount; ++i) {
+        logMsg += dumpRegister(i, registers[i]);
+        logMsg += ' ';
+    }
+    return logMsg;
+}
+
 QByteArray Thread::gdbRegisterLogMessage(bool verbose) const
 {
-    QByteArray logMsg = "REGISTER CONTENTS: (Thread 0x";
+    QByteArray logMsg = "Register contents: (Thread 0x";
     logMsg += QByteArray::number(id, 16);
     logMsg += " ) ";
-    if (verbose) {
-        for (int i = 0; i < RegisterCount; ++i) {
-            logMsg += dumpRegister(i, registers[i]);
-            logMsg += ' ';
-        }
-    }
+    if (verbose)
+        logMsg += registerContentsLogMessage();
     return logMsg;
 }
 
diff --git a/src/plugins/debugger/gdb/symbian.h b/src/plugins/debugger/gdb/symbian.h
index 74f0aaeba8e..30012183c53 100644
--- a/src/plugins/debugger/gdb/symbian.h
+++ b/src/plugins/debugger/gdb/symbian.h
@@ -110,6 +110,7 @@ struct Thread {
     void resetRegisters();
     // Gdb helpers for reporting values
     QByteArray gdbReportRegisters() const;
+    QByteArray registerContentsLogMessage() const;
     QByteArray gdbRegisterLogMessage(bool verbose) const;
     QByteArray gdbReportSingleRegister(unsigned i) const;
     QByteArray gdbSingleRegisterLogMessage(unsigned i) const;
diff --git a/src/plugins/debugger/gdb/tcftrkgdbadapter.cpp b/src/plugins/debugger/gdb/tcftrkgdbadapter.cpp
index 16af8f82a23..5ed26239d28 100644
--- a/src/plugins/debugger/gdb/tcftrkgdbadapter.cpp
+++ b/src/plugins/debugger/gdb/tcftrkgdbadapter.cpp
@@ -62,14 +62,6 @@
 
 enum { debug = 0 };
 
-// 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 {
 
@@ -138,14 +130,6 @@ TcfTrkGdbAdapter::TcfTrkGdbAdapter(GdbEngine *engine) :
         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()
@@ -282,8 +266,7 @@ void TcfTrkGdbAdapter::tcftrkEvent(const TcfTrkEvent &e)
             static_cast<const TcfTrkRunControlModuleLoadContextSuspendedEvent &>(e));
         break;
     case TcfTrkEvent::RunControlContextAdded: // Thread/process added
-        foreach(const RunControlContext &rc,
-            static_cast<const TcfTrkRunControlContextAddedEvent &>(e).contexts())
+        foreach(const RunControlContext &rc, static_cast<const TcfTrkRunControlContextAddedEvent &>(e).contexts())
             if (rc.type() == RunControlContext::Thread)
                 addThread(rc.threadId());
         break;
@@ -693,9 +676,11 @@ void TcfTrkGdbAdapter::handleGdbServerCommand(const QByteArray &cmd)
         // FIXME: Assume all goes well.
         m_snapshot.setRegisterValue(m_session.tid, regnumValue.first, regnumValue.second);
         logMessage(_("Setting register #%1 to 0x%2").arg(regnumValue.first).arg(regnumValue.second, 0, 16));
+        QByteArray registerValue;
+        trk::appendInt(&registerValue, trk::BigEndian); // Registers are big endian
         m_trkDevice->sendRegistersSetCommand(
             TcfTrkCallback(this, &TcfTrkGdbAdapter::handleWriteRegister),
-            currentThreadContextId(), regnumValue.first, regnumValue.second,
+            currentThreadContextId(), regnumValue.first, registerValue,
             QVariant(regnumValue.first));
         // Note that App TRK refuses to write registers 13 and 14
     }
@@ -1016,6 +1001,11 @@ void TcfTrkGdbAdapter::addThread(unsigned id)
             if (m_session.mainTid == 0)
                 m_session.mainTid = id;
         }
+        // We cannot retrieve register values unless the registers of that
+        // thread have been retrieved (TCF TRK oddity).
+        const QByteArray contextId = tcftrk::RunControlContext::tcfId(m_session.pid, id);
+        m_trkDevice->sendRegistersGetChildrenCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleRegisterChildren),
+                                                     contextId, QVariant(contextId));
     }
 }
 
@@ -1149,9 +1139,48 @@ void TcfTrkGdbAdapter::reportRegisters()
     sendGdbServerMessage(thread.gdbReportRegisters(), thread.gdbRegisterLogMessage(m_verbose));
 }
 
+void TcfTrkGdbAdapter::handleRegisterChildren(const tcftrk::TcfTrkCommandResult &result)
+{
+    const QByteArray contextId = result.cookie.toByteArray();
+    if (!result) {
+        logMessage("Error retrieving register children of " + contextId + ": " + result.errorString(), LogError);
+        return;
+    }
+    // Parse out registers.
+    // If this is a single 'pid.tid.rGPR' parent entry, recurse to get the actual registers,
+    // ('pid.tid.rGPR.R0'..). At least 'pid.tid.rGPR' must have been retrieved to be
+    // able to access the register contents.
+    QVector<QByteArray> registerNames = tcftrk::TcfTrkDevice::parseRegisterGetChildren(result);
+    if (registerNames.size() == 1) {
+        m_trkDevice->sendRegistersGetChildrenCommand(TcfTrkCallback(this, &TcfTrkGdbAdapter::handleRegisterChildren),
+                                                     registerNames.front(), result.cookie);
+        return;
+    }
+    // First thread: Set base names in device.
+    if (!m_trkDevice->registerNames().isEmpty())
+        return;
+    // Make sure we get all registers
+    const int registerCount = registerNames.size();
+    if (registerCount != Symbian::RegisterCount) {
+        logMessage(QString::fromLatin1("Invalid number of registers received, expected %1, got %2").
+                   arg(Symbian::RegisterCount).arg(registerCount), LogError);
+        return;
+    }
+    // Set up register names (strip thread context "pid.tid"+'.')
+    QString msg = QString::fromLatin1("Retrieved %1 register names: ").arg(registerCount);
+    const int contextLength = contextId.size() + 1;
+    for (int i = 0; i < registerCount; i++) {
+        registerNames[i].remove(0, contextLength);
+        if (i)
+            msg += QLatin1Char(',');
+        msg += QString::fromAscii(registerNames[i]);
+    }
+    logMessage(msg);
+    m_trkDevice->setRegisterNames(registerNames);
+}
+
 void TcfTrkGdbAdapter::handleReadRegisters(const TcfTrkCommandResult &result)
 {
-    logMessage("       REGISTER RESULT: " + result.toString());
     if (!result) {
         logMessage("ERROR: " + result.errorString(), LogError);
         return;
@@ -1163,8 +1192,10 @@ void TcfTrkGdbAdapter::handleReadRegisters(const TcfTrkCommandResult &result)
     unsigned i = result.cookie.toUInt();
     uint *registers = m_snapshot.registers(m_session.tid);
     QTC_ASSERT(registers, return;)
-    foreach (const JsonValue &jr, result.values.front().children())
-        registers[i++] = jr.data().toUInt(0, 16);
+    foreach (const JsonValue &jr, result.values.front().children()) {
+        QByteArray bigEndianRaw = QByteArray::fromBase64(jr.data());
+        registers[i++] = trk::extractInt(bigEndianRaw);
+    }
     m_snapshot.setRegistersValid(m_session.tid, true);
     if (debug)
         qDebug() << "handleReadRegisters: " << m_snapshot.toString();
@@ -1187,13 +1218,33 @@ void TcfTrkGdbAdapter::handleAndReportReadRegister(const TcfTrkCommandResult &re
         thread.gdbSingleRegisterLogMessage(registerNumber));
 }
 
+QByteArray TcfTrkGdbAdapter::stopMessage() const
+{
+    QByteArray logMsg = "Stopped with registers in thread 0x";
+    logMsg += QByteArray::number(m_session.tid, 16);
+    if (m_session.tid == m_session.mainTid)
+        logMsg += " [main]";
+    const int idx = m_snapshot.indexOfThread(m_session.tid);
+    if (idx == -1)
+        return logMsg;
+    const Symbian::Thread &thread = m_snapshot.threadInfo.at(idx);
+    logMsg += ", at 0x";
+    logMsg += QByteArray::number(thread.registers[Symbian::RegisterPC], 16);
+    logMsg += ", (loaded at 0x";
+    logMsg += QByteArray::number(m_session.codeseg, 16);
+    logMsg += ", offset 0x";
+    logMsg += QByteArray::number(thread.registers[Symbian::RegisterPC] - m_session.codeseg, 16);
+    logMsg += "), Register contents: ";
+    logMsg += thread.registerContentsLogMessage();
+    return logMsg;
+}
+
 void TcfTrkGdbAdapter::handleAndReportReadRegistersAfterStop(const TcfTrkCommandResult &result)
 {
     handleReadRegisters(result);
     handleReadRegisters(result);
     const bool reportThread = m_session.tid != m_session.mainTid;
-    sendGdbServerMessage(m_snapshot.gdbStopMessage(m_session.tid, reportThread),
-                         "Stopped with registers in thread " + QByteArray::number(m_session.tid, 16));
+    sendGdbServerMessage(m_snapshot.gdbStopMessage(m_session.tid, reportThread), stopMessage());
 }
 
 void TcfTrkGdbAdapter::handleAndReportSetBreakpoint(const TcfTrkCommandResult &result)
@@ -1249,13 +1300,20 @@ void TcfTrkGdbAdapter::handleReadMemoryBuffered(const TcfTrkCommandResult &resul
     const QByteArray memory = 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 bool error = !result;
+    const bool insufficient = unsigned(memory.size()) != range.size();
+    if (error || insufficient) {
+        QString msg = error ?
+                    QString::fromLatin1("Error reading memory: %1").arg(result.errorString()) :
+                    QString::fromLatin1("Error reading memory (got %1 of %2): %3").
+                    arg(memory.size()).arg(range.size()).arg(msgMemoryReadError(range.from, range.size()));
+        msg += QString::fromLatin1("\n(Retrying unbuffered...)");
+        logMessage(msg, LogError);
         // FIXME: This does not handle large requests properly.
         sendMemoryGetCommand(range, false);
         return;
     }
+
     m_snapshot.insertMemory(range, memory);
     tryAnswerGdbMemoryRequest(true);
 }
@@ -1267,11 +1325,15 @@ void TcfTrkGdbAdapter::handleReadMemoryUnbuffered(const TcfTrkCommandResult &res
     const QByteArray memory = 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());
+    const bool error = !result;
+    const bool insufficient = unsigned(memory.size()) != range.size();
+    if (error || insufficient) {
+        QString msg = error ?
+                    QString::fromLatin1("Error reading memory: %1").arg(result.errorString()) :
+                    QString::fromLatin1("Error reading memory (got %1 of %2): %3").
+                    arg(memory.size()).arg(range.size()).arg(msgMemoryReadError(range.from, range.size()));
+        logMessage(msg, LogError);
+        sendGdbServerMessage(QByteArray("E20"), msgMemoryReadError(32, range.from).toLatin1());
         return;
     }
     m_snapshot.insertMemory(range, memory);
diff --git a/src/plugins/debugger/gdb/tcftrkgdbadapter.h b/src/plugins/debugger/gdb/tcftrkgdbadapter.h
index 675faadfc0a..4dc9020c428 100644
--- a/src/plugins/debugger/gdb/tcftrkgdbadapter.h
+++ b/src/plugins/debugger/gdb/tcftrkgdbadapter.h
@@ -112,9 +112,11 @@ private:
     void handleWriteRegister(const tcftrk::TcfTrkCommandResult &result);
     void reportRegisters();
     void handleReadRegisters(const tcftrk::TcfTrkCommandResult &result);
+    void handleRegisterChildren(const tcftrk::TcfTrkCommandResult &result);
     void handleAndReportReadRegisters(const tcftrk::TcfTrkCommandResult &result);
     void handleAndReportReadRegister(const tcftrk::TcfTrkCommandResult &result);
     void handleAndReportReadRegistersAfterStop(const tcftrk::TcfTrkCommandResult &result);
+    QByteArray stopMessage() const;
     void handleAndReportSetBreakpoint(const tcftrk::TcfTrkCommandResult &result);
     void handleClearBreakpoint(const tcftrk::TcfTrkCommandResult &result);
     void readMemory(uint addr, uint len, bool buffered);
diff --git a/src/shared/symbianutils/tcftrkdevice.cpp b/src/shared/symbianutils/tcftrkdevice.cpp
index c77c12091e1..eb97691f728 100644
--- a/src/shared/symbianutils/tcftrkdevice.cpp
+++ b/src/shared/symbianutils/tcftrkdevice.cpp
@@ -90,24 +90,25 @@ bool TcfTrkCommandError::isError() const
 bool TcfTrkCommandError::parse(const QVector<JsonValue> &values)
 {
     // Parse an arbitrary hash (that could as well be a command response)
-    // and check for error elements.
+    // and check for error elements. It looks like sometimes errors are appended
+    // to other values.
     unsigned errorKeyCount = 0;
     clear();
     do {
-        if (values.isEmpty() || values.front().type() != JsonValue::Object)
+        if (values.isEmpty() || values.back().type() != JsonValue::Object)
             break;
-        foreach (const JsonValue &c, values.front().children()) {
+        foreach (const JsonValue &c, values.back().children()) {
             if (c.name() == "Time") {
                 timeMS = c.data().toULongLong();
                 errorKeyCount++;
             } else if (c.name() == "Code") {
-                code = c.data().toInt();
+                code = c.data().toLongLong();
                 errorKeyCount++;
             } else if (c.name() == "Format") {
                 format = c.data();
                 errorKeyCount++;
             } else if (c.name() == "AltCode") {
-                alternativeCode = c.data().toInt();
+                alternativeCode = c.data().toULongLong();
                 errorKeyCount++;
             } else if (c.name() == "AltOrg") {
                 alternativeOrganization = c.data();
@@ -121,7 +122,7 @@ bool TcfTrkCommandError::parse(const QVector<JsonValue> &values)
     if (debug) {
         qDebug("TcfTrkCommandError::parse: Found error %d (%u): ", errorFound, errorKeyCount);
         if (!values.isEmpty())
-            qDebug() << values.front().toString();
+            qDebug() << values.back().toString();
     }
     return errorFound;
 }
@@ -166,6 +167,7 @@ QString TcfTrkCommandResult::errorString() const
         return rc;
     case FailReply:
         str << "NAK";
+        return rc;
     case CommandErrorReply:
         commandError.write(str);
         break;
@@ -211,6 +213,11 @@ QString TcfTrkCommandResult::toString() const
     return rc;
 }
 
+// Entry for send queue.
+enum SpecialHandlingFlags { None =0,
+                            FakeRegisterGetMIntermediate = 0x1,
+                            FakeRegisterGetMFinal = 0x2 };
+
 struct TcfTrkSendQueueEntry
 {
     typedef TcfTrkDevice::MessageType MessageType;
@@ -218,10 +225,12 @@ struct TcfTrkSendQueueEntry
     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)  {}
+                                  const QByteArray &d,
+                                  const TcfTrkCallback &cb= TcfTrkCallback(),
+                                  const QVariant &ck = QVariant(),
+                                  unsigned sh = 0) :
+        messageType(mt), service(s), data(d), token(tok), cookie(ck), callback(cb),
+        specialHandling(sh) {}
 
     MessageType messageType;
     Services service;
@@ -229,6 +238,7 @@ struct TcfTrkSendQueueEntry
     int token;
     QVariant cookie;
     TcfTrkCallback callback;
+    unsigned specialHandling;
 };
 
 struct TcfTrkDevicePrivate {
@@ -246,6 +256,7 @@ struct TcfTrkDevicePrivate {
     QQueue<TcfTrkSendQueueEntry> m_sendQueue;
     TokenWrittenMessageMap m_writtenMessages;
     QVector<QByteArray> m_registerNames;
+    QVector<QByteArray> m_fakeGetMRegisterValues;
 };
 
 TcfTrkDevicePrivate::TcfTrkDevicePrivate() :
@@ -454,7 +465,8 @@ int TcfTrkDevice::parseTcfCommandReply(char type, const QVector<QByteArray> &tok
         return 236;
     }
     // No callback: remove entry from map, happy
-    if (!it.value().callback) {
+    const unsigned specialHandling = it.value().specialHandling;
+    if (!it.value().callback && specialHandling == 0u) {
         d->m_writtenMessages.erase(it);
         return 0;
     }
@@ -474,13 +486,53 @@ int TcfTrkDevice::parseTcfCommandReply(char type, const QVector<QByteArray> &tok
             }
         }
     }
-
     // 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);
+
+    // Check special handling
+    if (specialHandling) {
+        if (!result) {
+            qWarning("Error in special handling: %s", qPrintable(result.errorString()));
+            return -2;
+        }
+        // Fake 'Registers:getm': Store single register values in cache
+        if ((specialHandling & FakeRegisterGetMIntermediate)
+                || (specialHandling & FakeRegisterGetMFinal)) {
+            if (result.values.size() == 1) {
+                const int index = int(specialHandling) >> 16;
+                if (index >= 0 && index < d->m_fakeGetMRegisterValues.size()) {
+                    const QByteArray base64 = result.values.front().data();
+                    d->m_fakeGetMRegisterValues[index] = base64;
+                    if (d->m_verbose) {
+                        const QString msg = QString::fromLatin1("Caching register value #%1 '%2' 0x%3 (%4)").
+                                arg(index).arg(QString::fromAscii(d->m_registerNames.at(index))).
+                                arg(QString::fromAscii(QByteArray::fromBase64(base64).toHex())).
+                                arg(QString::fromAscii(base64));
+                        emitLogMessage(msg);
+                    }
+                }
+            }
+        }
+        // Fake 'Registers:getm' final value: Reformat entries as array and send off
+        if (specialHandling & FakeRegisterGetMFinal) {
+            QByteArray str;
+            str.append('[');
+            foreach(const QByteArray &rval, d->m_fakeGetMRegisterValues)
+                if (!rval.isEmpty()) {
+                    if (str.size() > 1)
+                        str.append(',');
+                    str.append('"');
+                    str.append(rval);
+                    str.append('"');
+                }
+            str.append(']');
+            result.values[0] = JsonValue(str);
+        }
+    }
+    if (it.value().callback)
+        it.value().callback(result);
     d->m_writtenMessages.erase(it);
-    callback(result);
     return 0;
 }
 
@@ -559,9 +611,20 @@ bool TcfTrkDevice::checkOpen()
 }
 
 void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
-                                     const char *commandParameters, int commandParametersLength,
+                                     const char *commandParameters, // may contain '\0'
+                                     int commandParametersLength,
                                      const TcfTrkCallback &callBack,
                                      const QVariant &cookie)
+{
+    sendTcfTrkMessage(mt, service, command, commandParameters, commandParametersLength,
+                      callBack, cookie, 0u);
+}
+
+void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service,
+                       const char *command,
+                       const char *commandParameters, int commandParametersLength,
+                       const TcfTrkCallback &callBack, const QVariant &cookie,
+                       unsigned specialHandling)
 
 {
     if (!checkOpen())
@@ -580,7 +643,7 @@ void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const cha
     data.append('\0');
     if (commandParametersLength)
         data.append(commandParameters, commandParametersLength);
-    const TcfTrkSendQueueEntry entry(mt, token, service, data, callBack, cookie);
+    const TcfTrkSendQueueEntry entry(mt, token, service, data, callBack, cookie, specialHandling);
     d->m_sendQueue.enqueue(entry);
     checkSendQueue();
 }
@@ -867,7 +930,7 @@ QByteArray TcfTrkDevice::parseMemoryGet(const TcfTrkCommandResult &r)
     // 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());
+        qWarning("TcfTrkDevice::parseMemoryGet(): Error retrieving memory: %s", r.values.at(1).toString(false).constData());
     // decode
     const QByteArray memory = QByteArray::fromBase64(memoryV.data());
     if (memory.isEmpty())
@@ -877,16 +940,97 @@ QByteArray TcfTrkDevice::parseMemoryGet(const TcfTrkCommandResult &r)
     return memory;
 }
 
+// Parse register children (array of names)
+QVector<QByteArray> TcfTrkDevice::parseRegisterGetChildren(const TcfTrkCommandResult &r)
+{
+    QVector<QByteArray> rc;
+    if (!r || r.values.size() < 1 || r.values.front().type() != JsonValue::Array)
+        return rc;
+    const JsonValue &front = r.values.front();
+    rc.reserve(front.childCount());
+    foreach(const JsonValue &v, front.children())
+        rc.push_back(v.data());
+    return rc;
+}
+
+void TcfTrkDevice::sendRegistersGetChildrenCommand(const TcfTrkCallback &callBack,
+                                     const QByteArray &contextId,
+                                     const QVariant &cookie)
+{
+    QByteArray data;
+    JsonInputStream str(data);
+    str << contextId;
+    sendTcfTrkMessage(MessageWithReply, RegistersService, "getChildren", data, callBack, cookie);
+}
+
+// Format id of register get request (needs contextId containing process and thread)
+static inline QByteArray registerId(const QByteArray &contextId, QByteArray id)
+{
+    QByteArray completeId = contextId;
+    if (!completeId.isEmpty())
+        completeId.append('.');
+    completeId.append(id);
+    return completeId;
+}
+
+// Format parameters of register get request
+static inline QByteArray registerGetData(const QByteArray &contextId, QByteArray id)
+{
+    QByteArray data;
+    JsonInputStream str(data);
+    str << registerId(contextId, id);
+    return data;
+}
+
+void TcfTrkDevice::sendRegistersGetCommand(const TcfTrkCallback &callBack,
+                                            const QByteArray &contextId,
+                                            QByteArray id,
+                                            const QVariant &cookie)
+{
+    sendTcfTrkMessage(MessageWithReply, RegistersService, "get",
+                      registerGetData(contextId, id), callBack, cookie);
+}
+
 void TcfTrkDevice::sendRegistersGetMCommand(const TcfTrkCallback &callBack,
                                             const QByteArray &contextId,
                                             const QVector<QByteArray> &ids,
                                             const QVariant &cookie)
 {
     // TODO: use "Registers" (which uses base64-encoded values)
+#if 0 // Once 'getm' is supported:
+    // Manually format the complete register ids as an array
     QByteArray data;
     JsonInputStream str(data);
-    str << contextId << '\0' << ids;
-    sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "get", data, callBack, cookie);
+    str << '[';
+    const int count = ids.size();
+    for (int r = 0; r < count; r++) {
+        if (r)
+            str << ',';
+        str << registerId(contextId, ids.at(r));
+    }
+    str << ']';
+    sendTcfTrkMessage(MessageWithReply, RegistersService, "getm", data, callBack, cookie);
+#else
+    // TCF TRK 4.0.5: Fake 'getm' by sending all requests, pass on callback to the last
+    // @Todo: Hopefully, we get 'getm'?
+    const int last = ids.size() - 1;
+    d->m_fakeGetMRegisterValues = QVector<QByteArray>(ids.size(), QByteArray());
+    for (int r = 0; r <= last; r++) {
+        const QByteArray data = registerGetData(contextId, ids.at(r));
+        // Determine special flags along with index
+        unsigned specialFlags = r == last ? FakeRegisterGetMFinal : FakeRegisterGetMIntermediate;
+        const int index = d->m_registerNames.indexOf(ids.at(r));
+        if (index == -1) { // Should not happen
+            qWarning("Invalid register name %s", ids.at(r).constData());
+            return;
+        }
+        specialFlags |= unsigned(index) << 16;
+        sendTcfTrkMessage(MessageWithReply, RegistersService, "get",
+                          data.constData(), data.size(),
+                          r == last ? callBack : TcfTrkCallback(),
+                          cookie, specialFlags);
+    }
+#endif
 }
 
 void TcfTrkDevice::sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
@@ -909,23 +1053,25 @@ void TcfTrkDevice::sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
 // Set register
 void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
                                            const QByteArray &contextId,
-                                           const QByteArray &id,
-                                           unsigned value,
+                                           QByteArray id,
+                                           const QByteArray &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);
+    if (!contextId.isEmpty()) {
+        id.prepend('.');
+        id.prepend(contextId);
+    }
+    str << id << '\0' << value.toBase64();
+    sendTcfTrkMessage(MessageWithReply, RegistersService, "set", data, callBack, cookie);
 }
 
 // Set register
 void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
                                            const QByteArray &contextId,
                                            unsigned registerNumber,
-                                           unsigned value,
+                                           const QByteArray &value,
                                            const QVariant &cookie)
 {
     if (registerNumber >= (unsigned)d->m_registerNames.size()) {
diff --git a/src/shared/symbianutils/tcftrkdevice.h b/src/shared/symbianutils/tcftrkdevice.h
index 6946c975163..1e0f94ce2bc 100644
--- a/src/shared/symbianutils/tcftrkdevice.h
+++ b/src/shared/symbianutils/tcftrkdevice.h
@@ -70,11 +70,11 @@ struct SYMBIANUTILS_EXPORT TcfTrkCommandError {
     bool parse(const QVector<JsonValue> &values);
 
     quint64 timeMS; // Since 1.1.1970
-    int code;
+    qint64 code;
     QByteArray format; // message
     // 'Alternative' meaning, like altOrg="POSIX"/altCode=<some errno>
     QByteArray alternativeOrganization;
-    int alternativeCode;
+    qint64 alternativeCode;
 };
 
 /* Answer to a Tcf command passed to the callback. */
@@ -112,6 +112,12 @@ http://dev.eclipse.org/svnroot/dsdp/org.eclipse.tm.tcf/trunk/docs/TCF%20Specific
 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.
+ *
+ * Note: As of 11.8.2010, TCF Trk 4.0.5 does not currently support 'Registers::getm'
+ * (get multiple registers). So, TcfTrkDevice emulates it by sending a sequence of
+ * single commands. As soon as 'Registers::getm' is natively supported, all code
+ * related to 'FakeRegisterGetm' should be removed. The workaround requires that
+ * the register name is known.
 */
 
 class SYMBIANUTILS_EXPORT TcfTrkDevice : public QObject
@@ -130,7 +136,9 @@ public:
 
     unsigned verbose() const;
 
-    // Mapping of register names for indices
+    // Mapping of register names to indices for multi-requests.
+    // Register names can be retrieved via 'Registers:getChildren' (requires
+    // context id to be stripped).
     QVector<QByteArray> registerNames() const;
     void setRegisterNames(const QVector<QByteArray>& n);
 
@@ -138,6 +146,7 @@ public:
     IODevicePtr takeDevice();
     void setDevice(const IODevicePtr &dp);
 
+    // Send with parameters from string (which may contain '\0').
     void sendTcfTrkMessage(MessageType mt, Services service,
                            const char *command,
                            const char *commandParameters, int commandParametersLength,
@@ -225,7 +234,18 @@ public:
                               quint64 start, const QByteArray& data,
                               const QVariant &cookie = QVariant());
 
-    // Reply is an array of hexvalues
+    // Get register names (children of context).
+    // It is possible to recurse from  thread id down to single registers.
+    void sendRegistersGetChildrenCommand(const TcfTrkCallback &callBack,
+                                         const QByteArray &contextId,
+                                         const QVariant &cookie = QVariant());
+
+    // Register get
+    void sendRegistersGetCommand(const TcfTrkCallback &callBack,
+                                 const QByteArray &contextId,
+                                 QByteArray id,
+                                 const QVariant &cookie);
+
     void sendRegistersGetMCommand(const TcfTrkCallback &callBack,
                                   const QByteArray &contextId,
                                   const QVector<QByteArray> &ids,
@@ -240,20 +260,21 @@ public:
     // Set register
     void sendRegistersSetCommand(const TcfTrkCallback &callBack,
                                  const QByteArray &contextId,
-                                 const QByteArray &ids,
-                                 unsigned value,
+                                 QByteArray ids,
+                                 const QByteArray &value, // binary value
                                  const QVariant &cookie = QVariant());
     // Set register
     void sendRegistersSetCommand(const TcfTrkCallback &callBack,
                                  const QByteArray &contextId,
                                  unsigned registerNumber,
-                                 unsigned value,
+                                 const QByteArray &value, // binary value
                                  const QVariant &cookie = QVariant());
 
     void sendLoggingAddListenerCommand(const TcfTrkCallback &callBack,
                                        const QVariant &cookie = QVariant());
 
     static QByteArray parseMemoryGet(const TcfTrkCommandResult &r);
+    static QVector<QByteArray> parseRegisterGetChildren(const TcfTrkCommandResult &r);
 
 signals:
     void genericTcfEvent(int service, const QByteArray &name, const QVector<tcftrk::JsonValue> &value);
@@ -278,6 +299,12 @@ private:
     int parseMessage(const QByteArray &);
     int parseTcfCommandReply(char type, const QVector<QByteArray> &tokens);
     int parseTcfEvent(const QVector<QByteArray> &tokens);
+    // Send with parameters from string (which may contain '\0').
+    void sendTcfTrkMessage(MessageType mt, Services service,
+                           const char *command,
+                           const char *commandParameters, int commandParametersLength,
+                           const TcfTrkCallback &callBack, const QVariant &cookie,
+                           unsigned specialHandling);
 
     TcfTrkDevicePrivate *d;
 };
diff --git a/src/shared/symbianutils/tcftrkmessage.cpp b/src/shared/symbianutils/tcftrkmessage.cpp
index 4aeb9387113..7b0822af4d7 100644
--- a/src/shared/symbianutils/tcftrkmessage.cpp
+++ b/src/shared/symbianutils/tcftrkmessage.cpp
@@ -36,7 +36,7 @@
 // Names matching the enum
 static const char *serviceNamesC[] =
 { "Locator", "RunControl", "Processes", "Memory", "Settings", "Breakpoints",
-  "Registers", "SimpleRegisters", "Logging",
+  "Registers", "Logging",
   "UnknownService"};
 
 namespace tcftrk {
diff --git a/src/shared/symbianutils/tcftrkmessage.h b/src/shared/symbianutils/tcftrkmessage.h
index 0a1aa06945a..b9e2ac420f7 100644
--- a/src/shared/symbianutils/tcftrkmessage.h
+++ b/src/shared/symbianutils/tcftrkmessage.h
@@ -52,7 +52,6 @@ enum Services {
     SettingsService,  // non-standard, trk specific
     BreakpointsService,
     RegistersService,
-    SimpleRegistersService, // non-standard, trk specific
     LoggingService,    // non-standard, trk specific
     UnknownService
 }; // Note: Check string array 'serviceNamesC' of same size when modifying this.
-- 
GitLab