summaryrefslogtreecommitdiff
path: root/src/plugins/debugger/gdb/codagdbadapter.cpp
diff options
context:
space:
mode:
authorPawel Polanski <pawel.3.polanski@nokia.com>2011-02-07 14:26:34 +0100
committerPawel Polanski <pawel.3.polanski@nokia.com>2011-02-07 15:38:11 +0100
commit532a8ad2e7dfac5d2e4bd7b57d78c176dad3d93c (patch)
tree51fe035f7f009fd17c72587a44ee0b3c24f1cc1f /src/plugins/debugger/gdb/codagdbadapter.cpp
parent3212e1152ceee303344b8ecd4d54372ba6ee0f98 (diff)
downloadqt-creator-532a8ad2e7dfac5d2e4bd7b57d78c176dad3d93c.tar.gz
Symbian: tcftrk renamed to Coda
Diffstat (limited to 'src/plugins/debugger/gdb/codagdbadapter.cpp')
-rw-r--r--src/plugins/debugger/gdb/codagdbadapter.cpp1614
1 files changed, 1614 insertions, 0 deletions
diff --git a/src/plugins/debugger/gdb/codagdbadapter.cpp b/src/plugins/debugger/gdb/codagdbadapter.cpp
new file mode 100644
index 0000000000..3792be545a
--- /dev/null
+++ b/src/plugins/debugger/gdb/codagdbadapter.cpp
@@ -0,0 +1,1614 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** No Commercial Usage
+**
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "codagdbadapter.h"
+
+#include "debuggerstartparameters.h"
+#include "codadevice.h"
+#include "trkutils.h"
+#include "gdbmi.h"
+#include "virtualserialdevice.h"
+
+#include "registerhandler.h"
+#include "threadshandler.h"
+#include "debuggercore.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 <utils/qtcprocess.h>
+
+#include <QtCore/QTimer>
+#include <QtCore/QDir>
+#include <QtNetwork/QTcpServer>
+#include <QtNetwork/QTcpSocket>
+
+#ifdef Q_OS_WIN
+# include "dbgwinutils.h"
+#else
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+#define CB(callback) \
+ static_cast<GdbEngine::AdapterCallback>(&CodaGdbAdapter::callback), \
+ STRINGIFY(callback)
+
+enum { debug = 0 };
+
+/* Libraries we want to be notified about (pending introduction of a 'notify all'
+ * setting in TCF TRK, Bug #11842 */
+static const char *librariesC[] = {
+"pipelib.ldd", "rpipe.dll", "libc.dll",
+"libdl.dll", "libm.dll", "libpthread.dll",
+"libssl.dll", "libz.dll", "libzcore.dll", "libstdcpp.dll",
+"sqlite3.dll", "phonon_mmf.dll", "QtCore.dll", "QtXml.dll", "QtGui.dll",
+"QtNetwork.dll", "QtTest.dll", "QtSql.dll", "QtSvg.dll", "phonon.dll",
+"QtScript.dll", "QtXmlPatterns.dll", "QtMultimedia.dll", "qjpeg.dll",
+"qgif.dll", "qmng.dll", "qtiff.dll", "qico.dll", "qsvg.dll",
+"qcncodecs.dll", "qjpcodecs.dll","qtwcodecs.dll", "qkrcodecs.dll", "qsvgicon.dll",
+"qts60plugin_5_0.dll", "QtWebKit.dll"};
+
+namespace Debugger {
+namespace Internal {
+
+using namespace Symbian;
+using namespace Coda;
+
+static inline QString startMsg(const trk::Session &session)
+{
+ return CodaGdbAdapter::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);
+}
+
+/* -------------- CodaGdbAdapter:
+ * Startup-sequence:
+ * - startAdapter connects both sockets/devices
+ * - In the TCF Locator Event, gdb is started and the engine is notified
+ * that the adapter has started.
+ * - Engine calls setupInferior(), 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'.
+ * Thread handling (30.06.2010):
+ * TRK does not report thread creation/termination. So, if we receive
+ * a stop in a different thread, we store an additional thread in snapshot.
+ * When continuing in sendTrkContinue(), we delete this thread, since we cannot
+ * know whether it will exist at the next stop.
+ * Also note that threads continue running in Symbian even if one crashes.
+ * TODO: - Maybe thread reporting will be improved in TCF TRK?
+ * - Stop all threads once one stops?
+ * - Breakpoints do not trigger in threads other than the main thread. */
+
+CodaGdbAdapter::CodaGdbAdapter(GdbEngine *engine) :
+ AbstractGdbAdapter(engine),
+ m_running(false),
+ m_stopReason(0),
+ m_trkDevice(new CodaDevice(this)),
+ m_gdbAckMode(true),
+ m_uid(0),
+ m_verbose(0),
+ m_firstResumableExeLoadedEvent(false),
+ m_registerRequestPending(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 unsigned long portOffset = winGetCurrentProcessId() % 100;
+#else
+ const uid_t portOffset = getuid();
+#endif
+ m_gdbServerName = _("127.0.0.1:%1").arg(2222 + portOffset);
+
+ setVerbose(debuggerCore()->boolSetting(VerboseLog) ? 1 : 0);
+
+ connect(debuggerCore()->action(VerboseLog), SIGNAL(valueChanged(QVariant)),
+ this, SLOT(setVerbose(QVariant)));
+ connect(m_trkDevice, SIGNAL(error(QString)),
+ this, SLOT(codaDeviceError(QString)));
+ connect(m_trkDevice, SIGNAL(logMessage(QString)),
+ this, SLOT(trkLogMessage(QString)));
+ connect(m_trkDevice, SIGNAL(tcfEvent(Coda::CodaEvent)),
+ this, SLOT(codaEvent(Coda::CodaEvent)));
+}
+
+CodaGdbAdapter::~CodaGdbAdapter()
+{
+ cleanup();
+ logMessage("Shutting down.\n");
+}
+
+void CodaGdbAdapter::setVerbose(const QVariant &value)
+{
+ setVerbose(value.toInt());
+}
+
+void CodaGdbAdapter::setVerbose(int verbose)
+{
+ if (debug)
+ qDebug("CodaGdbAdapter::setVerbose %d", verbose);
+ m_verbose = verbose;
+ m_trkDevice->setVerbose(m_verbose);
+}
+
+void CodaGdbAdapter::trkLogMessage(const QString &msg)
+{
+ logMessage(_("TRK ") + msg);
+}
+
+void CodaGdbAdapter::setGdbServerName(const QString &name)
+{
+ m_gdbServerName = name;
+}
+
+// Split a TCP address specification '<addr>[:<port>]'
+static QPair<QString, unsigned short> splitIpAddressSpec(const QString &addressSpec, unsigned short defaultPort = 0)
+{
+ const int pos = addressSpec.indexOf(QLatin1Char(':'));
+ if (pos == -1)
+ return QPair<QString, unsigned short>(addressSpec, defaultPort);
+ const QString address = addressSpec.left(pos);
+ bool ok;
+ const unsigned short port = addressSpec.mid(pos + 1).toUShort(&ok);
+ if(!ok) {
+ qWarning("Invalid IP address specification: '%s', defaulting to port %hu.", qPrintable(addressSpec), defaultPort);
+ return QPair<QString, unsigned short>(addressSpec, defaultPort);
+ }
+ return QPair<QString, unsigned short>(address, port);
+}
+
+void CodaGdbAdapter::handleCodaRunControlModuleLoadContextSuspendedEvent(const CodaRunControlModuleLoadContextSuspendedEvent &se)
+{
+ m_snapshot.resetMemory();
+ const ModuleLoadEventInfo &minfo = se.info();
+ // Register in session, keep modules and libraries in sync.
+ const QString moduleName = QString::fromUtf8(minfo.name);
+ const bool isExe = moduleName.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive);
+ // Add to shared library list
+ if (!isExe) {
+ if (minfo.loaded) {
+ m_session.modules.push_back(moduleName);
+ trk::Library library;
+ library.name = minfo.name;
+ library.codeseg = minfo.codeAddress;
+ library.dataseg = minfo.dataAddress;
+ library.pid = RunControlContext::processIdFromTcdfId(se.id());
+ m_session.libraries.push_back(library);
+ // Load local symbol file into gdb provided there is one
+ if (library.codeseg) {
+ const QString localSymFileName = Symbian::localSymFileForLibrary(library.name,
+ m_symbolFileFolder);
+ if (!localSymFileName.isEmpty()) {
+ showMessage(Symbian::msgLoadLocalSymFile(localSymFileName, library.name, library.codeseg), LogMisc);
+ m_engine->postCommand(Symbian::symFileLoadCommand(localSymFileName, library.codeseg, library.dataseg));
+ } // has local sym
+ } // code seg
+
+ } 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 setupInferior2(),
+ if (isExe && m_firstResumableExeLoadedEvent) {
+ m_firstResumableExeLoadedEvent = false;
+ m_session.codeseg = minfo.codeAddress;
+ m_session.dataseg = minfo.dataAddress;
+ logMessage(startMsg(m_session), LogMisc);
+ // 26.8.2010: When paging occurs in S^3, bogus starting ROM addresses
+ // like 0x500000 or 0x40000 are reported. Warn about symbol resolution errors.
+ // Code duplicated in TrkAdapter. @TODO: Hopefully fixed in future TRK versions.
+ if ((m_session.codeseg & 0xFFFFF) == 0) {
+ const QString warnMessage = tr("The reported code segment address (0x%1) might be invalid. Symbol resolution or setting breakoints may not work.").
+ arg(m_session.codeseg, 0, 16);
+ logMessage(warnMessage, LogError);
+ }
+
+ const QByteArray symbolFile = m_symbolFile.toLocal8Bit();
+ if (symbolFile.isEmpty()) {
+ logMessage(_("WARNING: No symbol file available."), LogError);
+ } 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 + "\"");
+ }
+ foreach (const QByteArray &s, Symbian::gdbStartupSequence())
+ m_engine->postCommand(s);
+ 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(CodaCallback(), se.id());
+ }
+ }
+}
+
+void CodaGdbAdapter::handleTargetRemote(const GdbResponse &record)
+{
+ QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
+ if (record.resultClass == GdbResultDone) {
+ m_engine->handleInferiorPrepared();
+ 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());
+ m_engine->notifyInferiorSetupFailed(msg);
+ }
+}
+
+void CodaGdbAdapter::codaEvent(const CodaEvent &e)
+{
+ if (debug)
+ qDebug() << e.toString() << m_session.toString() << m_snapshot.toString();
+ logMessage(e.toString());
+
+ switch (e.type()) {
+ case CodaEvent::LocatorHello:
+ m_trkDevice->sendLoggingAddListenerCommand(CodaCallback());
+ startGdb(); // Commands are only accepted after hello
+ break;
+ case CodaEvent::RunControlModuleLoadSuspended: // A module was loaded
+ handleCodaRunControlModuleLoadContextSuspendedEvent(
+ static_cast<const CodaRunControlModuleLoadContextSuspendedEvent &>(e));
+ break;
+ case CodaEvent::RunControlContextAdded: // Thread/process added
+ foreach(const RunControlContext &rc, static_cast<const CodaRunControlContextAddedEvent &>(e).contexts())
+ if (rc.type() == RunControlContext::Thread)
+ addThread(rc.threadId());
+ break;
+ case CodaEvent::RunControlContextRemoved: // Thread/process removed
+ foreach (const QByteArray &id,
+ static_cast<const CodaRunControlContextRemovedEvent &>(e).ids())
+ switch (RunControlContext::typeFromTcfId(id)) {
+ case RunControlContext::Thread:
+ m_snapshot.removeThread(RunControlContext::threadIdFromTcdfId(id));
+ break;
+ case RunControlContext::Process:
+ sendGdbServerMessage("W00", "Process exited");
+ break;
+ }
+ break;
+ case CodaEvent::RunControlSuspended: {
+ // Thread suspended/stopped
+ const CodaRunControlContextSuspendedEvent &se =
+ static_cast<const CodaRunControlContextSuspendedEvent &>(e);
+ const unsigned threadId = RunControlContext::threadIdFromTcdfId(se.id());
+ const QString reason = QString::fromUtf8(se.reasonID());
+ const QString message = QString::fromUtf8(se.message()).replace(QLatin1String("\n"), QLatin1String(", "));
+ showMessage(_("Thread %1 stopped: '%2': %3").
+ arg(threadId).arg(reason, message), LogMisc);
+ // Stopped in a new thread: Add.
+ m_snapshot.reset();
+ m_session.tid = threadId;
+ if (m_snapshot.indexOfThread(threadId) == -1)
+ m_snapshot.addThread(threadId);
+ m_snapshot.setThreadState(threadId, reason);
+ // Update registers first, then report stopped
+ m_running = false;
+ m_stopReason = reason.contains(QLatin1String("exception"), Qt::CaseInsensitive)
+ || reason.contains(QLatin1String("panic"), Qt::CaseInsensitive) ?
+ gdbServerSignalSegfault : gdbServerSignalTrap;
+ m_trkDevice->sendRegistersGetMRangeCommand(
+ CodaCallback(this, &CodaGdbAdapter::handleAndReportReadRegistersAfterStop),
+ currentThreadContextId(), 0,
+ Symbian::RegisterCount);
+ }
+ break;
+ case Coda::CodaEvent::LoggingWriteEvent: // TODO: Not tested yet.
+ showMessage(e.toString(), AppOutput);
+ break;
+ default:
+ break;
+ }
+}
+
+void CodaGdbAdapter::startGdb()
+{
+ QStringList gdbArgs;
+ gdbArgs.append(QLatin1String("--nx")); // Do not read .gdbinit file
+ if (!m_engine->startGdb(gdbArgs, QString(), QString())) {
+ cleanup();
+ return;
+ }
+ m_engine->handleAdapterStarted();
+}
+
+void CodaGdbAdapter::codaDeviceError(const QString &errorString)
+{
+ logMessage(errorString);
+ if (state() == EngineSetupRequested) {
+ m_engine->handleAdapterStartFailed(errorString, QString());
+ } else {
+ m_engine->handleAdapterCrashed(errorString);
+ }
+}
+
+void CodaGdbAdapter::logMessage(const QString &msg, int channel)
+{
+ if (m_verbose || channel != LogDebug)
+ showMessage(msg, channel);
+ if (debug)
+ qDebug("%s", qPrintable(msg));
+}
+
+//
+// Gdb
+//
+void CodaGdbAdapter::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 CodaGdbAdapter::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 CodaGdbAdapter::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 CodaGdbAdapter::sendGdbServerAck()
+{
+ if (!m_gdbAckMode)
+ return;
+ logMessage("gdb: <- +");
+ sendGdbServerPacket(QByteArray(1, '+'), false);
+}
+
+void CodaGdbAdapter::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;
+}
+
+void CodaGdbAdapter::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.
+ bool ok = false;
+ const uint signalNumber = cmd.mid(1).toUInt(&ok, 16);
+ //TODO: Meaning of the message is not clear.
+ sendGdbServerAck();
+ logMessage(_("Not implemented 'Continue with signal' %1: ").arg(signalNumber), LogWarning);
+ sendGdbServerMessage("O" + QByteArray("Console output").toHex());
+ sendGdbServerMessage("W81"); // "Process exited with result 1
+ sendTrkContinue();
+ }
+
+ else if (cmd.startsWith('D')) {
+ sendGdbServerAck();
+ sendGdbServerMessage("OK", "shutting down");
+ }
+
+ else if (cmd == "g") {
+ // Read general registers.
+ logMessage(msgGdbPacket(QLatin1String("Read registers")));
+ if (m_snapshot.registersValid(m_session.tid)) {
+ sendGdbServerAck();
+ reportRegisters();
+ } else {
+ sendGdbServerAck();
+ if (m_trkDevice->registerNames().isEmpty()) {
+ m_registerRequestPending = true;
+ } else {
+ sendRegistersGetMCommand();
+ }
+ }
+ }
+
+ else if (cmd == "gg") {
+ // Force re-reading general registers for debugging purpose.
+ sendGdbServerAck();
+ m_snapshot.setRegistersValid(m_session.tid, false);
+ sendRegistersGetMCommand();
+ }
+
+ else if (cmd.startsWith("salstep,")) {
+ // Receive address range for current line for future use when stepping.
+ sendGdbServerAck();
+ m_snapshot.parseGdbStepRange(cmd, 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();
+ m_snapshot.parseGdbStepRange(cmd, true);
+ sendGdbServerMessage("", msgStepRangeReceived(m_snapshot.lineFromAddress, m_snapshot.lineToAddress, m_snapshot.stepOver));
+ }
+
+ else if (cmd.startsWith("Hc")) {
+ sendGdbServerAck();
+ gdbSetCurrentThread(cmd, "Set current thread for step & continue ");
+ }
+
+ else if (cmd.startsWith("Hg")) {
+ sendGdbServerAck();
+ gdbSetCurrentThread(cmd, "Set current thread ");
+ }
+
+ else if (cmd == "k" || cmd.startsWith("vKill")) {
+ // Kill inferior process
+ logMessage(msgGdbPacket(QLatin1String("kill")));
+ sendRunControlTerminateCommand();
+ }
+
+ else if (cmd.startsWith('m')) {
+ logMessage(msgGdbPacket(QLatin1String("Read memory")));
+ // m addr,length
+ sendGdbServerAck();
+ const QPair<quint64, unsigned> addrLength = parseGdbReadMemoryRequest(cmd);
+ if (addrLength.first && addrLength.second) {
+ readMemory(addrLength.first, addrLength.second, m_bufferedMemoryRead);
+ } else {
+ sendGdbServerMessage("E20", "Error " + cmd);
+ }
+ }
+
+ else if (cmd.startsWith('X')) { // Write memory
+ const int dataPos = cmd.indexOf(':');
+ if (dataPos == -1) {
+ sendGdbServerMessage("E20", "Error (colon expected) " + cmd);
+ return;
+ }
+ const QPair<quint64, unsigned> addrLength =
+ parseGdbReadMemoryRequest(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(_("Writing %1 bytes from 0x%2: %3").
+ arg(addrLength.second).arg(addrLength.first, 0, 16).
+ arg(QString::fromAscii(data.toHex())));
+ m_trkDevice->sendMemorySetCommand(
+ CodaCallback(this, &CodaGdbAdapter::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);
+ const int threadIndex = m_snapshot.indexOfThread(m_session.tid);
+ QTC_ASSERT(threadIndex != -1, return)
+ const Symbian::Thread &thread = m_snapshot.threadInfo.at(threadIndex);
+ if (thread.registerValid) {
+ sendGdbServerMessage(thread.gdbReportSingleRegister(registerNumber), thread.gdbSingleRegisterLogMessage(registerNumber));
+ } else {
+ //qDebug() << "Fetching single register";
+ m_trkDevice->sendRegistersGetMRangeCommand(
+ CodaCallback(this, &CodaGdbAdapter::handleAndReportReadRegister),
+ currentThreadContextId(), registerNumber, 1);
+ }
+ }
+
+ else if (cmd.startsWith('P')) {
+ logMessage(msgGdbPacket(QLatin1String("write register")));
+ // $Pe=70f96678#d3
+ sendGdbServerAck();
+ const QPair<uint, uint> regnumValue = parseGdbWriteRegisterWriteRequest(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(
+ CodaCallback(this, &CodaGdbAdapter::handleWriteRegister),
+ currentThreadContextId(), regnumValue.first, registerValue,
+ QVariant(regnumValue.first));
+ // 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
+ sendGdbServerAck();
+ sendGdbServerMessage("QC" + QByteArray::number(m_session.tid, 16));
+ }
+
+ else if (cmd.startsWith("qSupported")) {
+ //$qSupported#37
+ //$qSupported:multiprocess+#c6
+ //logMessage("Handling 'qSupported'");
+ sendGdbServerAck();
+ sendGdbServerMessage(Symbian::gdbQSupported);
+ }
+
+ // Tracepoint handling as of gdb 7.2 onwards
+ else if (cmd == "qTStatus") { // Tracepoints
+ sendGdbServerAck();
+ sendGdbServerMessage("T0;tnotrun:0", QByteArray("No trace experiment running"));
+ }
+ // Trace variables as of gdb 7.2 onwards
+ else if (cmd == "qTfV" || cmd == "qTsP" || cmd == "qTfP") {
+ sendGdbServerAck();
+ sendGdbServerMessage("l", QByteArray("No trace points"));
+ }
+
+ else if (cmd.startsWith("qThreadExtraInfo")) {
+ // $qThreadExtraInfo,1f9#55
+ sendGdbServerAck();
+ sendGdbServerMessage(m_snapshot.gdbQThreadExtraInfo(cmd));
+ }
+
+ 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();
+ sendGdbServerMessage(m_session.gdbQsDllInfo(), "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(Symbian::gdbArchitectureXml);
+ }
+
+ else if (cmd == "qfThreadInfo") {
+ // That's the _first_ query package.
+ sendGdbServerAck();
+ sendGdbServerMessage(m_snapshot.gdbQsThreadInfo(), "thread information transferred");
+ }
+
+ 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")) {
+ sendGdbServerAck();
+ sendGdbServerMessage(m_session.gdbLibraryList(), "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")) {
+ const uint pc = m_snapshot.registerValue(m_session.tid, RegisterPC);
+ logMessage(msgGdbPacket(_("Step range from 0x%1").
+ arg(pc, 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 QPair<quint64, unsigned> addrLen = parseGdbSetBreakpointRequest(cmd);
+ if (addrLen.first) {
+ //qDebug() << "ADDR: " << hexNumber(addr) << " LEN: " << len;
+ logMessage(_("Inserting breakpoint at 0x%1, %2")
+ .arg(addrLen.first, 0, 16).arg(addrLen.second));
+ // const QByteArray ba = trkBreakpointMessage(addr, len, len == 4);
+ Coda::Breakpoint bp(addrLen.first);
+ bp.size = addrLen.second;
+ bp.setContextId(m_session.pid);
+ // We use the automatic ids calculated from the location
+ // address instead of the map in snapshot.
+ m_trkDevice->sendBreakpointsAddCommand(
+ CodaCallback(this, &CodaGdbAdapter::handleAndReportSetBreakpoint),
+ bp);
+ } else {
+ logMessage("MISPARSED BREAKPOINT '" + cmd + "'')" , LogError);
+ }
+ }
+
+ 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(
+ CodaCallback(this, &CodaGdbAdapter::handleClearBreakpoint),
+ Coda::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 CodaGdbAdapter::sendRunControlTerminateCommand()
+{
+ // Requires id of main thread to terminate.
+ // Note that calling 'Settings|set|removeExecutable' crashes TCF TRK,
+ // so, it is apparently not required.
+ m_trkDevice->sendRunControlTerminateCommand(CodaCallback(this, &CodaGdbAdapter::handleRunControlTerminate),
+ mainThreadContextId());
+}
+
+void CodaGdbAdapter::handleRunControlTerminate(const Coda::CodaCommandResult &)
+{
+ QString msg = QString::fromLatin1("CODA disconnected");
+ const bool emergencyShutdown = m_gdbProc.state() != QProcess::Running;
+ if (emergencyShutdown)
+ msg += QString::fromLatin1(" (emergency shutdown");
+ logMessage(msg);
+ if (emergencyShutdown) {
+ cleanup();
+ m_engine->notifyAdapterShutdownOk();
+ }
+}
+
+void CodaGdbAdapter::gdbSetCurrentThread(const QByteArray &cmd, const char *why)
+{
+ // Thread ID from Hg/Hc commands: '-1': All, '0': arbitrary, else hex thread id.
+ const QByteArray id = cmd.mid(2);
+ const int threadId = id == "-1" ? -1 : id.toInt(0, 16);
+ const QByteArray message = QByteArray(why) + QByteArray::number(threadId);
+ logMessage(msgGdbPacket(_(message)));
+ // Set thread for subsequent operations (`m', `M', `g', `G', et.al.).
+ // for 'other operations. 0 - any thread
+ //$Hg0#df
+ m_session.tid = threadId <= 0 ? m_session.mainTid : uint(threadId);
+ sendGdbServerMessage("OK", message);
+}
+
+void CodaGdbAdapter::interruptInferior()
+{
+ m_trkDevice->sendRunControlSuspendCommand(CodaCallback(), m_tcfProcessId);
+}
+
+void CodaGdbAdapter::startAdapter()
+{
+ m_snapshot.fullReset();
+ m_session.reset();
+ m_firstResumableExeLoadedEvent = true;
+ m_tcfProcessId.clear();
+
+ // Retrieve parameters
+ const DebuggerStartParameters &parameters = startParameters();
+ m_remoteExecutable = parameters.executable;
+ m_remoteArguments = Utils::QtcProcess::splitArgs(parameters.processArgs);
+ m_symbolFile = parameters.symbolFileName;
+ if (!m_symbolFile.isEmpty())
+ m_symbolFileFolder = QFileInfo(m_symbolFile).absolutePath();
+
+ QPair<QString, unsigned short> codaAddress;
+
+ QSharedPointer<QTcpSocket> codaSocket;
+ if (parameters.communicationChannel == DebuggerStartParameters::CommunicationChannelTcpIp) {
+ codaSocket = QSharedPointer<QTcpSocket>(new QTcpSocket);
+ m_trkDevice->setDevice(codaSocket);
+ m_trkIODevice = codaSocket;
+ } else {
+ QSharedPointer<SymbianUtils::VirtualSerialDevice> serialDevice(new SymbianUtils::VirtualSerialDevice(parameters.remoteChannel));
+ m_trkDevice->setSerialFrame(true);
+ m_trkDevice->setDevice(serialDevice);
+ bool ok = serialDevice->open(QIODevice::ReadWrite);
+ if (!ok) {
+ QString msg = QString("Couldn't open serial device: %1.")
+ .arg(serialDevice->errorString());
+ logMessage(msg, LogError);
+ m_engine->handleAdapterStartFailed(msg, QString());
+ return;
+ }
+ m_trkIODevice = serialDevice;
+ }
+
+ if (debug)
+ qDebug() << parameters.processArgs;
+
+ m_uid = parameters.executableUid;
+ codaAddress = QPair<QString, unsigned short>(parameters.serverAddress, parameters.serverPort);
+// m_remoteArguments.clear(); FIXME: Should this be here?
+
+ // Unixish gdbs accept only forward slashes
+ m_symbolFile.replace(QLatin1Char('\\'), QLatin1Char('/'));
+ // Start
+ QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
+ showMessage(_("TRYING TO START ADAPTER"));
+ logMessage(QLatin1String("### Starting CodaGdbAdapter"));
+
+ QTC_ASSERT(m_gdbServer == 0, delete m_gdbServer);
+ QTC_ASSERT(m_gdbConnection == 0, m_gdbConnection = 0);
+ m_gdbServer = new QTcpServer(this);
+
+ const QPair<QString, unsigned short> address = splitIpAddressSpec(m_gdbServerName);
+ if (!m_gdbServer->listen(QHostAddress(address.first), address.second)) {
+ QString msg = QString("Unable to start the gdb server at %1: %2.")
+ .arg(m_gdbServerName).arg(m_gdbServer->errorString());
+ logMessage(msg, LogError);
+ m_engine->handleAdapterStartFailed(msg, QString());
+ return;
+ }
+
+ logMessage(QString("Gdb server running on %1.\nLittle endian assumed.")
+ .arg(m_gdbServerName));
+
+ connect(m_gdbServer, SIGNAL(newConnection()),
+ this, SLOT(handleGdbConnection()));
+
+ if (parameters.communicationChannel == DebuggerStartParameters::CommunicationChannelTcpIp) {
+ logMessage(_("Connecting to TCF TRK on %1:%2")
+ .arg(codaAddress.first).arg(codaAddress.second));
+ codaSocket->connectToHost(codaAddress.first, codaAddress.second);
+ } else {
+ m_trkDevice->sendSerialPing(false);
+ }
+}
+
+void CodaGdbAdapter::setupInferior()
+{
+ QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
+
+ // Compile additional libraries
+ QStringList libraries;
+ const unsigned libraryCount = sizeof(librariesC)/sizeof(char *);
+ for (unsigned i = 0; i < libraryCount; i++)
+ libraries.push_back(QString::fromAscii(librariesC[i]));
+
+ m_trkDevice->sendProcessStartCommand(
+ CodaCallback(this, &CodaGdbAdapter::handleCreateProcess),
+ m_remoteExecutable, m_uid, m_remoteArguments,
+ QString(), true, libraries);
+}
+
+void CodaGdbAdapter::addThread(unsigned id)
+{
+ showMessage(QString::fromLatin1("Thread %1 reported").arg(id), LogMisc);
+ // Make thread known, register as main if it is the first one.
+ if (m_snapshot.indexOfThread(id) == -1) {
+ m_snapshot.addThread(id);
+ if (m_session.tid == 0) {
+ m_session.tid = 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 = Coda::RunControlContext::tcfId(m_session.pid, id);
+ m_trkDevice->sendRegistersGetChildrenCommand(CodaCallback(this, &CodaGdbAdapter::handleRegisterChildren),
+ contextId, QVariant(contextId));
+ }
+}
+
+void CodaGdbAdapter::handleCreateProcess(const CodaCommandResult &result)
+{
+ if (debug)
+ qDebug() << "ProcessCreated: " << result.toString();
+ if (!result) {
+ const QString errorMessage = result.errorString();
+ logMessage(_("Failed to start process: %1").arg(errorMessage), LogError);
+ m_engine->notifyInferiorSetupFailed(result.errorString());
+ return;
+ }
+ QTC_ASSERT(!result.values.isEmpty(), return);
+
+ RunControlContext ctx;
+ ctx.parse(result.values.front());
+ logMessage(ctx.toString());
+
+ m_session.pid = ctx.processId();
+ m_tcfProcessId = RunControlContext::tcfId(m_session.pid);
+ if (const unsigned threadId = ctx.threadId())
+ addThread(threadId);
+ // See ModuleLoadSuspendedEvent for the rest.
+ m_session.codeseg = 0;
+ m_session.dataseg = 0;
+}
+
+void CodaGdbAdapter::runEngine()
+{
+ QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
+ m_engine->notifyEngineRunAndInferiorStopOk();
+ // Trigger the initial "continue" manually.
+ m_engine->continueInferiorInternal();
+}
+
+//
+// AbstractGdbAdapter interface implementation
+//
+
+void CodaGdbAdapter::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(_("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 CodaGdbAdapter::cleanup()
+{
+ delete m_gdbServer;
+ m_gdbServer = 0;
+ if (!m_trkIODevice.isNull()) {
+ QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(m_trkIODevice.data());
+ const bool isOpen = socket
+ ? socket->state() == QAbstractSocket::ConnectedState
+ : m_trkIODevice->isOpen();
+ if (isOpen) {
+ sendRunControlTerminateCommand(); //ensure process is stopped after being suspended
+ if (socket) {
+ socket->disconnect();
+ } else {
+ m_trkIODevice->close();
+ }
+ }
+ } //!m_trkIODevice.isNull()
+}
+
+void CodaGdbAdapter::shutdownInferior()
+{
+ m_engine->defaultInferiorShutdown("kill");
+}
+
+void CodaGdbAdapter::shutdownAdapter()
+{
+ if (m_gdbProc.state() == QProcess::Running) {
+ cleanup();
+ m_engine->notifyAdapterShutdownOk();
+ } else {
+ // Something is wrong, gdb crashed. Kill debuggee (see handleDeleteProcess2)
+ if (m_trkDevice->device()->isOpen()) {
+ logMessage("Emergency shutdown of CODA", LogError);
+ sendRunControlTerminateCommand();
+ }
+ }
+}
+
+void CodaGdbAdapter::trkReloadRegisters()
+{
+ // Take advantage of direct access to cached register values.
+ m_snapshot.syncRegisters(m_session.tid, m_engine->registerHandler());
+}
+
+void CodaGdbAdapter::trkReloadThreads()
+{
+ m_snapshot.syncThreads(m_engine->threadsHandler());
+}
+
+void CodaGdbAdapter::handleWriteRegister(const CodaCommandResult &result)
+{
+ const int registerNumber = result.cookie.toInt();
+ if (result) {
+ sendGdbServerMessage("OK");
+ } else {
+ logMessage(_("ERROR writing register #%1: %2")
+ .arg(registerNumber).arg(result.errorString()), LogError);
+ sendGdbServerMessage("E01");
+ }
+}
+
+void CodaGdbAdapter::sendRegistersGetMCommand()
+{
+ // Send off a register command, which requires the names to be present.
+ QTC_ASSERT(!m_trkDevice->registerNames().isEmpty(), return )
+
+ m_trkDevice->sendRegistersGetMRangeCommand(
+ CodaCallback(this, &CodaGdbAdapter::handleAndReportReadRegisters),
+ currentThreadContextId(), 0,
+ Symbian::RegisterCount);
+}
+
+void CodaGdbAdapter::reportRegisters()
+{
+ const int threadIndex = m_snapshot.indexOfThread(m_session.tid);
+ QTC_ASSERT(threadIndex != -1, return);
+ const Symbian::Thread &thread = m_snapshot.threadInfo.at(threadIndex);
+ sendGdbServerMessage(thread.gdbReportRegisters(), thread.gdbRegisterLogMessage(m_verbose));
+}
+
+void CodaGdbAdapter::handleRegisterChildren(const Coda::CodaCommandResult &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 = Coda::CodaDevice::parseRegisterGetChildren(result);
+ if (registerNames.size() == 1) {
+ m_trkDevice->sendRegistersGetChildrenCommand(CodaCallback(this, &CodaGdbAdapter::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);
+ if (m_registerRequestPending) { // Request already pending?
+ logMessage(_("Resuming registers request after receiving register names..."));
+ sendRegistersGetMCommand();
+ }
+}
+
+void CodaGdbAdapter::handleReadRegisters(const CodaCommandResult &result)
+{
+ // check for errors
+ if (!result) {
+ logMessage("ERROR: " + result.errorString(), LogError);
+ return;
+ }
+ if (result.values.isEmpty() || result.values.front().type() != JsonValue::String) {
+ logMessage(_("Format error in register message: ") + result.toString(), LogError);
+ return;
+ }
+
+ unsigned i = result.cookie.toUInt();
+ // TODO: When reading 8-byte floating-point registers is supported, thread registers won't be an array of uints
+ uint *registers = m_snapshot.registers(m_session.tid);
+ QTC_ASSERT(registers, return;)
+
+ QByteArray bigEndianRaw = QByteArray::fromBase64(result.values.front().data());
+ // TODO: When reading 8-byte floating-point registers is supported, will need to know the list
+ // of registers and lengths of each register
+ for (int j = 0; j < bigEndianRaw.size(); j += 4) {
+ registers[i++] = ((bigEndianRaw.at(j ) & 0xff) << 24) +
+ ((bigEndianRaw.at(j + 1) & 0xff) << 16) +
+ ((bigEndianRaw.at(j + 2) & 0xff) << 8) +
+ (bigEndianRaw.at(j + 3) & 0xff);
+ }
+
+ m_snapshot.setRegistersValid(m_session.tid, true);
+ if (debug)
+ qDebug() << "handleReadRegisters: " << m_snapshot.toString();
+}
+
+void CodaGdbAdapter::handleAndReportReadRegisters(const CodaCommandResult &result)
+{
+ handleReadRegisters(result);
+ reportRegisters();
+}
+
+void CodaGdbAdapter::handleAndReportReadRegister(const CodaCommandResult &result)
+{
+ handleReadRegisters(result);
+ const uint registerNumber = result.cookie.toUInt();
+ const int threadIndex = m_snapshot.indexOfThread(m_session.tid);
+ QTC_ASSERT(threadIndex != -1, return);
+ const Symbian::Thread &thread = m_snapshot.threadInfo.at(threadIndex);
+ sendGdbServerMessage(thread.gdbReportSingleRegister(registerNumber),
+ thread.gdbSingleRegisterLogMessage(registerNumber));
+}
+
+QByteArray CodaGdbAdapter::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 CodaGdbAdapter::handleAndReportReadRegistersAfterStop(const CodaCommandResult &result)
+{
+ handleReadRegisters(result);
+ handleReadRegisters(result);
+ const bool reportThread = m_session.tid != m_session.mainTid;
+ sendGdbServerMessage(m_snapshot.gdbStopMessage(m_session.tid, m_stopReason, reportThread), stopMessage());
+}
+
+void CodaGdbAdapter::handleAndReportSetBreakpoint(const CodaCommandResult &result)
+{
+ if (result) {
+ sendGdbServerMessage("OK");
+ } else {
+ logMessage(QLatin1String("Error setting breakpoint: ") + result.errorString(), LogError);
+ sendGdbServerMessage("E21");
+ }
+}
+
+void CodaGdbAdapter::handleClearBreakpoint(const CodaCommandResult &result)
+{
+ logMessage("CLEAR BREAKPOINT ");
+ if (!result)
+ logMessage("Error clearing breakpoint: " + result.errorString(), LogError);
+ sendGdbServerMessage("OK");
+}
+
+void CodaGdbAdapter::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);
+}
+
+void CodaGdbAdapter::sendMemoryGetCommand(const MemoryRange &range, bool buffered)
+{
+ const QVariant cookie = QVariant::fromValue(range);
+ const CodaCallback cb = buffered ?
+ CodaCallback(this, &CodaGdbAdapter::handleReadMemoryBuffered) :
+ CodaCallback(this, &CodaGdbAdapter::handleReadMemoryUnbuffered);
+ m_trkDevice->sendMemoryGetCommand(cb, currentThreadContextId(), range.from, range.size(), cookie);
+}
+
+void CodaGdbAdapter::handleReadMemoryBuffered(const CodaCommandResult &result)
+{
+ QTC_ASSERT(qVariantCanConvert<MemoryRange>(result.cookie), return);
+
+ const QByteArray memory = CodaDevice::parseMemoryGet(result);
+ const MemoryRange range = result.cookie.value<MemoryRange>();
+
+ 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);
+}
+
+void CodaGdbAdapter::handleReadMemoryUnbuffered(const CodaCommandResult &result)
+{
+ QTC_ASSERT(qVariantCanConvert<MemoryRange>(result.cookie), return);
+
+ const QByteArray memory = CodaDevice::parseMemoryGet(result);
+ const MemoryRange range = result.cookie.value<MemoryRange>();
+
+ 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);
+ tryAnswerGdbMemoryRequest(false);
+}
+
+void CodaGdbAdapter::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(),
+ m_snapshot.memoryReadLogMessage(wanted.from, m_session.tid, m_verbose, 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 CodaGdbAdapter::handleWriteMemory(const CodaCommandResult &result)
+{
+ if (result) {
+ sendGdbServerMessage("OK", "Write memory");
+ } else {
+ logMessage(QLatin1String("Error writing memory: ") + result.errorString(), LogError);
+ sendGdbServerMessage("E21");
+ }
+}
+
+QByteArray CodaGdbAdapter::mainThreadContextId() const
+{
+ return RunControlContext::tcfId(m_session.pid, m_session.mainTid);
+}
+
+QByteArray CodaGdbAdapter::currentThreadContextId() const
+{
+ return RunControlContext::tcfId(m_session.pid, m_session.tid);
+}
+
+void CodaGdbAdapter::sendTrkContinue()
+{
+ // Remove all but main thread as we do not know whether they will exist
+ // at the next stop.
+ if (m_snapshot.threadInfo.size() > 1)
+ m_snapshot.threadInfo.remove(1, m_snapshot.threadInfo.size() - 1);
+ m_trkDevice->sendRunControlResumeCommand(CodaCallback(), m_tcfProcessId);
+}
+
+void CodaGdbAdapter::sendTrkStepRange()
+{
+ uint from = m_snapshot.lineFromAddress;
+ uint to = m_snapshot.lineToAddress;
+ const uint pc = m_snapshot.registerValue(m_session.tid, RegisterPC);
+ if (from <= pc && pc <= to) {
+ const QString msg = _("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 RunControlResumeMode mode = (from == to && to == pc) ?
+ (m_snapshot.stepOver ? RM_STEP_OVER : RM_STEP_INTO) :
+ (m_snapshot.stepOver ? RM_STEP_OVER_RANGE : RM_STEP_INTO_RANGE);
+
+ logMessage(_("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(
+ CodaCallback(this, &CodaGdbAdapter::handleStep),
+ currentThreadContextId(),
+ mode, 1, from, to);
+}
+
+void CodaGdbAdapter::handleStep(const CodaCommandResult &result)
+{
+
+ if (!result) { // Try fallback with Continue.
+ logMessage(_("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