Skip to content
Snippets Groups Projects
trkgdbadapter.cpp 47.8 KiB
Newer Older
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

hjk's avatar
hjk committed
#include "trkgdbadapter.h"
#ifndef STANDALONE_RUNNER
#include "gdbengine.h"
#endif
#ifndef Q_OS_WIN
#  include <sys/types.h>
#  include <unistd.h>
#endif
hjk's avatar
hjk committed
#define TrkCB(s) TrkCallback(this, &TrkGdbAdapter::s)


using namespace trk;

enum { KnownRegisters = RegisterPSGdb + 1};

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(int n, uint value)
{
    QByteArray ba;
    ba += ' ';
    if (n < KnownRegisters && registerNames[n]) {
        ba += registerNames[n];
    } else {
        ba += '#';
        ba += QByteArray::number(n);
    }
    ba += "=" + hexxNumber(value);
    return ba;
}

namespace Debugger {
namespace Internal {

hjk's avatar
hjk committed
TrkGdbAdapter::TrkGdbAdapter()
{
    m_running = false;
    m_gdbAckMode = true;
hjk's avatar
hjk committed
    m_verbose = 2;
    m_serialFrame = false;
hjk's avatar
hjk committed
    m_bufferedMemoryRead = true;
    m_rfcommDevice = "/dev/rfcomm0";

    uid_t userId = getuid();
    m_gdbServerName = QString("127.0.0.1:%1").arg(2222 + userId);

    connect(&m_gdbProc, SIGNAL(readyReadStandardError()),
hjk's avatar
hjk committed
        this, SIGNAL(readyReadStandardError()));
    connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()),
hjk's avatar
hjk committed
        this, SIGNAL(readyReadStandardOutput()));
    connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
        this, SLOT(handleGdbError(QProcess::ProcessError)));
    connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
        this, SLOT(handleGdbFinished(int, QProcess::ExitStatus)));
    connect(&m_gdbProc, SIGNAL(started()),
        this, SLOT(handleGdbStarted()));
    connect(&m_gdbProc, SIGNAL(stateChanged(QProcess::ProcessState)),
        this, SLOT(handleGdbStateChanged(QProcess::ProcessState)));

    connect(&m_rfcommProc, SIGNAL(readyReadStandardError()),
        this, SLOT(handleRfcommReadyReadStandardError()));
    connect(&m_rfcommProc, SIGNAL(readyReadStandardOutput()),
        this, SLOT(handleRfcommReadyReadStandardOutput()));
    connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
        this, SLOT(handleRfcommError(QProcess::ProcessError)));
    connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
        this, SLOT(handleRfcommFinished(int, QProcess::ExitStatus)));
    connect(&m_gdbProc, SIGNAL(started()),
        this, SLOT(handleRfcommStarted()));
    connect(&m_gdbProc, SIGNAL(stateChanged(QProcess::ProcessState)),
        this, SLOT(handleRfcommStateChanged(QProcess::ProcessState)));

    if (m_verbose > 1)
        m_trkDevice.setVerbose(true);
    m_trkDevice.setSerialFrame(m_serialFrame);

    connect(&m_trkDevice, SIGNAL(logMessage(QString)),
        this, SLOT(trkLogMessage(QString)));
}

hjk's avatar
hjk committed
TrkGdbAdapter::~TrkGdbAdapter()
{
    m_gdbServer.close();
hjk's avatar
hjk committed
    logMessage("Shutting down.\n");
hjk's avatar
hjk committed
void TrkGdbAdapter::trkLogMessage(const QString &msg)
{
    logMessage("TRK " + msg);
}

hjk's avatar
hjk committed
void TrkGdbAdapter::setGdbServerName(const QString &name)
{
    m_gdbServerName = name;
}

hjk's avatar
hjk committed
QString TrkGdbAdapter::gdbServerIP() const
{
    int pos = m_gdbServerName.indexOf(':');
    if (pos == -1)
        return m_gdbServerName;
    return m_gdbServerName.left(pos);
}

hjk's avatar
hjk committed
uint TrkGdbAdapter::gdbServerPort() const
{
    int pos = m_gdbServerName.indexOf(':');
    if (pos == -1)
        return 0;
    return m_gdbServerName.mid(pos + 1).toUInt();
}

hjk's avatar
hjk committed
QByteArray TrkGdbAdapter::trkContinueMessage()
{
    QByteArray ba;
    appendInt(&ba, m_session.pid);
    appendInt(&ba, m_session.tid);
    return ba;
}

hjk's avatar
hjk committed
QByteArray TrkGdbAdapter::trkReadRegisterMessage()
{
    QByteArray ba;
    appendByte(&ba, 0); // Register set, only 0 supported
    appendShort(&ba, 0);
    appendShort(&ba, RegisterCount - 1); // last register
    appendInt(&ba, m_session.pid);
    appendInt(&ba, m_session.tid);
    return ba;
}

hjk's avatar
hjk committed
QByteArray TrkGdbAdapter::trkReadMemoryMessage(uint addr, uint len)
{
    QByteArray ba;
    appendByte(&ba, 0x08); // Options, FIXME: why?
    appendShort(&ba, len);
    appendInt(&ba, addr);
    appendInt(&ba, m_session.pid);
    appendInt(&ba, m_session.tid);
    return ba;
}

hjk's avatar
hjk committed
QByteArray TrkGdbAdapter::trkStepRangeMessage(byte option)
{
    QByteArray ba;
    appendByte(&ba, option);
    appendInt(&ba, m_snapshot.registers[RegisterPC]); // start address
    appendInt(&ba, m_snapshot.registers[RegisterPC]); // end address
    appendInt(&ba, m_session.pid);
    appendInt(&ba, m_session.tid);
    return ba;
}

hjk's avatar
hjk committed
void TrkGdbAdapter::startInferior()
{
    QString errorMessage;
    if (!m_trkDevice.open(m_rfcommDevice, &errorMessage)) {
hjk's avatar
hjk committed
        emit output("LOOPING");
        QTimer::singleShot(1000, this, SLOT(startInferior()));
        return;
    }

    m_trkDevice.sendTrkInitialPing();
    sendTrkMessage(0x01); // Connect
    sendTrkMessage(0x05, TrkCB(handleSupportMask));
    sendTrkMessage(0x06, TrkCB(handleCpuType));
    sendTrkMessage(0x04, TrkCB(handleTrkVersions)); // Versions
    //sendTrkMessage(0x09); // Unrecognized command
    //sendTrkMessage(0x4a, 0,
    //    "10 " + formatString("C:\\data\\usingdlls.sisx")); // Open File
    //sendTrkMessage(0x4B, 0, "00 00 00 01 73 1C 3A C8"); // Close File

    QByteArray ba;
    appendByte(&ba, 0); // ?
    appendByte(&ba, 0); // ?
    appendByte(&ba, 0); // ?

    QByteArray file("C:\\sys\\bin\\filebrowseapp.exe");
    appendString(&ba, file, TargetByteOrder);
    sendTrkMessage(0x40, TrkCB(handleCreateProcess), ba); // Create Item
    //sendTrkMessage(TRK_WRITE_QUEUE_NOOP_CODE, TrkCB(startGdbServer));
}

hjk's avatar
hjk committed
void TrkGdbAdapter::logMessage(const QString &msg)
hjk's avatar
hjk committed
    if (m_verbose)
        emit output(msg);
hjk's avatar
hjk committed
void TrkGdbAdapter::handleGdbConnection()
{
    logMessage("HANDLING GDB CONNECTION");

    m_gdbConnection = m_gdbServer.nextPendingConnection();
    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;
}

hjk's avatar
hjk committed
void TrkGdbAdapter::readGdbServerCommand()
{
    QByteArray packet = m_gdbConnection->readAll();
    m_gdbReadBuffer.append(packet);

    logMessage("gdb: -> " + 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");
            continue;
        }

        if (code == char(0x03)) {
            logMessage("INTERRUPT RECEIVED");
            interruptInferior();
            continue;
        }

        if (code != '$') {
            logMessage("Broken package (2) " + quoteUnprintableLatin1(ba)
                + hexNumber(code));
            continue;
        }

        int pos = ba.indexOf('#');
        if (pos == -1) {
            logMessage("Invalid checksum format in "
                + quoteUnprintableLatin1(ba));
            continue;
        }

        bool ok = false;
        uint checkSum = ba.mid(pos + 1, 2).toUInt(&ok, 16);
        if (!ok) {
            logMessage("Invalid checksum format 2 in "
                + quoteUnprintableLatin1(ba));
            return;
        }

        //logMessage(QString("Packet checksum: %1").arg(checkSum));
        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));
        }

        QByteArray cmd = ba.left(pos);
        ba.remove(0, pos + 3);
        handleGdbServerCommand(cmd);
    }
}

hjk's avatar
hjk committed
bool TrkGdbAdapter::sendGdbServerPacket(const QByteArray &packet, bool doFlush)
{
    if (!m_gdbConnection) {
        logMessage(QString::fromLatin1("Cannot write to gdb: No connection (%1)")
hjk's avatar
hjk committed
            .arg(QString::fromLatin1(packet)));
        return false;
    }
    if (m_gdbConnection->state() != QAbstractSocket::ConnectedState) {
        logMessage(QString::fromLatin1("Cannot write to gdb: Not connected (%1)")
hjk's avatar
hjk committed
            .arg(QString::fromLatin1(packet)));
        return false;
    }
    if (m_gdbConnection->write(packet) == -1) {
        logMessage(QString::fromLatin1("Cannot write to gdb: %1 (%2)")
hjk's avatar
hjk committed
            .arg(m_gdbConnection->errorString()).arg(QString::fromLatin1(packet)));
        return false;
    }
    if (doFlush)
        m_gdbConnection->flush();
    return true;
}

hjk's avatar
hjk committed
void TrkGdbAdapter::sendGdbServerAck()
{
    if (!m_gdbAckMode)
        return;
    QByteArray packet = "+";
    logMessage("gdb: <- " + packet);
    sendGdbServerPacket(packet, false);
}

hjk's avatar
hjk committed
void TrkGdbAdapter::sendGdbServerMessage(const QByteArray &msg, const QByteArray &logNote)
{
    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: <- " + packet + QByteArray(pad, ' ') + logNote);
    sendGdbServerPacket(packet, true);
}

hjk's avatar
hjk committed
void TrkGdbAdapter::sendGdbServerMessageAfterTrkResponse(const QByteArray &msg,
    const QByteArray &logNote)
{
    QByteArray ba = msg + char(1) + logNote;
    sendTrkMessage(TRK_WRITE_QUEUE_NOOP_CODE, TrkCB(reportToGdb), "", ba); // Answer gdb
}

hjk's avatar
hjk committed
void TrkGdbAdapter::reportToGdb(const TrkResult &result)
{
    QByteArray message = result.cookie.toByteArray();
    QByteArray note;
    int pos = message.lastIndexOf(char(1)); // HACK
    if (pos != -1) {
        note = message.mid(pos + 1);
        message = message.left(pos);
    }
    message.replace("@CODESEG@", hexNumber(m_session.codeseg));
    message.replace("@DATASEG@", hexNumber(m_session.dataseg));
    message.replace("@PID@", hexNumber(m_session.pid));
    message.replace("@TID@", hexNumber(m_session.tid));
    sendGdbServerMessage(message, note);
}

hjk's avatar
hjk committed
QByteArray TrkGdbAdapter::trkBreakpointMessage(uint addr, uint len, bool armMode)
{
    QByteArray ba;
    appendByte(&ba, 0x82);  // unused option
    appendByte(&ba, armMode /*bp.mode == ArmMode*/ ? 0x00 : 0x01);
    appendInt(&ba, addr);
    appendInt(&ba, len);
    appendInt(&ba, 0x00000001);
    appendInt(&ba, m_session.pid);
    appendInt(&ba, 0xFFFFFFFF);
    return ba;
}

hjk's avatar
hjk committed
void TrkGdbAdapter::handleGdbServerCommand(const QByteArray &cmd)
{
    // 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();
        QByteArray ba;
        appendByte(&ba, 0); // options
        appendInt(&ba, 0); // start address
        appendInt(&ba, 0); // end address
        appendInt(&ba, m_session.pid);
        appendInt(&ba, m_session.tid);
        sendTrkMessage(0x18, TrkCallback(), ba);
    }

    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;
        uint signalNumber = cmd.mid(1).toInt(&ok, 16);
        QByteArray ba;
        appendInt(&ba, m_session.pid);
        appendInt(&ba, m_session.tid);
hjk's avatar
hjk committed
        sendTrkMessage(0x18, TrkCB(handleSignalContinue), ba, signalNumber);
    }

    else if (cmd.startsWith("D")) {
        sendGdbServerAck();
        sendGdbServerMessage("OK", "shutting down");
    }

    else if (cmd == "g") {
        // Read general registers.
hjk's avatar
hjk committed
        logMessage(msgGdbPacket(QLatin1String("Read registers")));
        sendGdbServerAck();
        sendTrkMessage(0x12, TrkCB(handleAndReportReadRegisters),
            trkReadRegisterMessage());
    }

    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).toInt(0, 16);
        sendGdbServerMessage("OK", "Set current thread "
            + QByteArray::number(m_session.currentThread));
    }

hjk's avatar
hjk committed
    else if (cmd == "k" || cmd.startsWith("vKill")) {
        // Kill inferior process
        logMessage(msgGdbPacket(QLatin1String("kill")));
        sendGdbServerAck();
        QByteArray ba;
        appendByte(&ba, 0); // ?
        appendByte(&ba, 0); // Sub-command: Delete Process
        appendInt(&ba, m_session.pid);
        sendTrkMessage(0x41, TrkCallback(), ba, "Delete process"); // Delete Item
        sendGdbServerMessageAfterTrkResponse("", "process killed");
    }

    else if (cmd.startsWith("m")) {
        logMessage(msgGdbPacket(QLatin1String("Read memory")));
        // m addr,length
        sendGdbServerAck();
        uint addr = 0, len = 0;
        do {
            const int pos = cmd.indexOf(',');
            if (pos == -1)
                break;
            bool ok;
            addr = cmd.mid(1, pos - 1).toUInt(&ok, 16);
            if (!ok)
                break;
            len = cmd.mid(pos + 1).toUInt(&ok, 16);
            if (!ok)
                break;
        } while (false);
        if (len) {
            readMemory(addr, len);
        } else {
            sendGdbServerMessage("E20", "Error " + cmd);
        }
    }
    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).toInt(&ok, 16);
        QByteArray logMsg = "Read Register";
        if (registerNumber == RegisterPSGdb) {
            QByteArray ba;
hjk's avatar
hjk committed
            appendInt(&ba, m_snapshot.registers[RegisterPSTrk], LittleEndian);
            logMsg += dumpRegister(registerNumber, m_snapshot.registers[RegisterPSTrk]);
            sendGdbServerMessage(ba.toHex(), logMsg);
hjk's avatar
hjk committed
        } else if (registerNumber < 16) {
            QByteArray ba;
hjk's avatar
hjk committed
            appendInt(&ba, m_snapshot.registers[registerNumber], LittleEndian);
            logMsg += dumpRegister(registerNumber, m_snapshot.registers[registerNumber]);
            sendGdbServerMessage(ba.toHex(), logMsg);
        } else {
hjk's avatar
hjk committed
            //sendGdbServerMessage("0000", "read single unknown register #"
            //    + QByteArray::number(registerNumber));
            sendGdbServerMessage("E01", "read single unknown register");
        }
    }

    else if (cmd == "qAttached") {
        //$qAttached#8f
        // 1: attached to an existing process
        // 0: created a new process
        sendGdbServerAck();
        sendGdbServerMessage("0", "new process created");
        //sendGdbServerMessage("1", "attached to existing process");
        //sendGdbServerMessage("E01", "new process created");
    }

    else if (cmd.startsWith("qC")) {
        logMessage(msgGdbPacket(QLatin1String("query thread id")));
        // Return the current thread ID
        //$qC#b4
        sendGdbServerAck();
        sendGdbServerMessageAfterTrkResponse("QC@TID@");
    }

    else if (cmd.startsWith("qSupported")) {
        //$qSupported#37
        //$qSupported:multiprocess+#c6
        //logMessage("Handling 'qSupported'");
        sendGdbServerAck();
hjk's avatar
hjk committed
        sendGdbServerMessage(
            "PacketSize=7cf;"
            "QPassSignals+;"
            "qXfer:libraries:read+;"
            //"qXfer:auxv:read+;"
            "qXfer:features:read+");
    }

    else if (cmd == "qfDllInfo") {
        // happens with  gdb 6.4.50.20060226-cvs / CodeSourcery
        // never made it into FSF gdb?
        sendGdbServerAck();
        sendGdbServerMessage("", "FIXME: nothing?");
    }

    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();
        sendGdbServerMessageAfterTrkResponse("TextSeg=@CODESEG@;DataSeg=@DATASEG@");
    }

    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
hjk's avatar
hjk committed
            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>symbianelf</architecture></target>");
    }

    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(QLatin1String("Step range")));
        logMessage("  from " + hexxNumber(m_snapshot.registers[RegisterPC]));
        sendGdbServerAck();
        m_running = true;
hjk's avatar
hjk committed
        QByteArray ba = trkStepRangeMessage(0x01);  // options "step into"
        sendTrkMessage(0x19, TrkCB(handleStepInto), ba, "Step range");
    }

    else if (cmd == "vCont?") {
        // actions supported by the vCont packet
        sendGdbServerAck();
        //sendGdbServerMessage("OK"); // we don't support vCont.
hjk's avatar
hjk committed
        sendGdbServerMessage("vCont;c;C;s;S;n");
    }

    else if (cmd == "vCont;c") {
        // vCont[;action[:thread-id]]...'
        sendGdbServerAck();
        m_running = true;
        sendTrkMessage(0x18, TrkCallback(), trkContinueMessage(), "CONTINUE");
    }

    else if (cmd.startsWith("Z0,") || cmd.startsWith("Z1,")) {
        // Insert breakpoint
hjk's avatar
hjk committed
        sendGdbServerAck();
        logMessage(msgGdbPacket(QLatin1String("Insert breakpoint")));
        // $Z0,786a4ccc,4#99
        const int pos = cmd.lastIndexOf(',');
        bool ok = false;
        const uint addr = cmd.mid(3, pos - 3).toInt(&ok, 16);
        const uint len = cmd.mid(pos + 1).toInt(&ok, 16);
        //qDebug() << "ADDR: " << hexNumber(addr) << " LEN: " << len;
        logMessage(QString::fromLatin1("Inserting breakpoint at 0x%1, %2")
            .arg(addr, 0, 16).arg(len));
        const QByteArray ba = trkBreakpointMessage(addr, len, m_session.pid);
        sendTrkMessage(0x1B, TrkCB(handleAndReportSetBreakpoint), ba, addr);
    }

    else if (cmd.startsWith("z0,") || cmd.startsWith("z1,")) {
        // Remove breakpoint
hjk's avatar
hjk committed
        sendGdbServerAck();
        logMessage(msgGdbPacket(QLatin1String("Remove breakpoint")));
        // $z0,786a4ccc,4#99
        const int pos = cmd.lastIndexOf(',');
        bool ok = false;
        const uint addr = cmd.mid(3, pos - 3).toInt(&ok, 16);
        const uint len = cmd.mid(pos + 1).toInt(&ok, 16);
        const uint bp = m_session.addressToBP[addr];
        if (bp == 0) {
            logMessage(QString::fromLatin1("NO RECORDED BP AT 0x%1, %2")
                .arg(addr, 0, 16).arg(len));
hjk's avatar
hjk committed
            sendGdbServerMessage("E00");
        } else {
            m_session.addressToBP.remove(addr);
            QByteArray ba;
            appendByte(&ba, 0x00);
            appendShort(&ba, bp);
            appendInt(&ba, addr);
            sendTrkMessage(0x1C, TrkCB(handleClearBreakpoint), ba, 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)
                    .toInt(&ok1, 16);
                const int length = data.mid(commaPos + 1).toInt(&ok2, 16);
                if (ok1 && ok2) {
                    const QString msg = QString::fromLatin1("Read of OS auxilary "
                        "vector (%1, %2) not implemented.").arg(offset).arg(length);
hjk's avatar
hjk committed
                    logMessage(msgGdbPacket(msg));
                    sendGdbServerMessage("E20", msg.toLatin1());
                    handled = true;
                }
            }
        } // auxv read
        if (!handled) {
            const QString msg = QLatin1String("FIXME unknown 'XFER'-request: ")
                + QString::fromAscii(cmd);
hjk's avatar
hjk committed
            logMessage(msgGdbPacket(msg));
            sendGdbServerMessage("E20", msg.toLatin1());
        }
    } // qPart/qXfer
    else {
        logMessage(msgGdbPacket(QLatin1String("FIXME unknown: ")
            + QString::fromAscii(cmd)));
    }
}

hjk's avatar
hjk committed
void TrkGdbAdapter::executeCommand(const QString &msg)
{
    if (msg == "EI") {
        sendGdbMessage("-exec-interrupt");
    } else if (msg == "C") {
        sendTrkMessage(0x18, TrkCallback(), trkContinueMessage(), "CONTINUE");
hjk's avatar
hjk committed
    } else if (msg == "S") {
        sendTrkMessage(0x19, TrkCallback(), trkStepRangeMessage(0x01), "STEP");
    } else if (msg == "N") {
        sendTrkMessage(0x19, TrkCallback(), trkStepRangeMessage(0x11), "NEXT");
    } else if (msg == "R") {
hjk's avatar
hjk committed
        sendTrkMessage(0x12, TrkCB(handleReadRegisters),
            trkReadRegisterMessage(), "READ REGS");
    } else if (msg == "I") {
        interruptInferior();
    } else {
        logMessage("EXECUTING GDB COMMAND " + msg);
        sendGdbMessage(msg);
    }
}

hjk's avatar
hjk committed
void TrkGdbAdapter::sendTrkMessage(byte code, TrkCallback callback,
    const QByteArray &data, const QVariant &cookie)
{
    m_trkDevice.sendTrkMessage(code, callback, data, cookie);
}

hjk's avatar
hjk committed
void TrkGdbAdapter::sendTrkAck(byte token)
{
    logMessage(QString("SENDING ACKNOWLEDGEMENT FOR TOKEN %1").arg(int(token)));
    m_trkDevice.sendTrkAck(token);
}

hjk's avatar
hjk committed
void TrkGdbAdapter::handleTrkError(const QString &msg)
{
    logMessage("## TRK ERROR: " + msg);
}

hjk's avatar
hjk committed
void TrkGdbAdapter::handleTrkResult(const TrkResult &result)
{
    if (result.isDebugOutput) {
        sendTrkAck(result.token);
        logMessage(QLatin1String("APPLICATION OUTPUT: ") +
            QString::fromAscii(result.data));
        sendGdbServerMessage("O" + result.data.toHex());
        return;
    }
    logMessage("READ TRK " + result.toString());
    QByteArray prefix = "READ BUF:                                       ";
    QByteArray str = result.toString().toUtf8();
    switch (result.code) {
        case 0x80: // ACK
            break;
        case 0xff: { // NAK. This mostly means transmission error, not command failed.
            QString logMsg;
            QTextStream(&logMsg) << prefix << "NAK: for token=" << result.token
                << " ERROR: " << errorMessage(result.data.at(0)) << ' ' << str;
hjk's avatar
hjk committed
            logMessage(logMsg);
            break;
        }
        case 0x90: { // Notified Stopped
            // 90 01   78 6a 40 40   00 00 07 23   00 00 07 24  00 00
            const char *data = result.data.data();
            const uint addr = extractInt(data);
            const uint pid = extractInt(data + 4);
            const uint tid = extractInt(data + 8);
            logMessage(prefix + QString::fromLatin1("NOTE: PID %1/TID %2 "
                "STOPPED at 0x%3").arg(pid).arg(tid).arg(addr, 0, 16));
            sendTrkAck(result.token);
            if (addr) {
                // Todo: Do not send off GdbMessages if a synced gdb
                // query is pending, queue instead
                if (m_running) {
                    m_running = false;
hjk's avatar
hjk committed
                    // We almost always need register values, so get them
                    // now before informing gdb about the stop. In theory
                    //sendGdbServerMessage("S05", "Target stopped");
                    sendTrkMessage(0x12,
                        TrkCB(handleAndReportReadRegistersAfterStop),
                        trkReadRegisterMessage());
                }
            } else {
                logMessage(QLatin1String("Ignoring stop at 0"));
            }
            break;
        }
        case 0x91: { // Notify Exception (obsolete)
            logMessage(prefix + "NOTE: EXCEPTION  " + str);
            sendTrkAck(result.token);
            break;
        }
        case 0x92: { //
            logMessage(prefix + "NOTE: INTERNAL ERROR: " + str);
            sendTrkAck(result.token);
            break;
        }

        // target->host OS notification
        case 0xa0: { // Notify Created
            const char *data = result.data.data();
            const byte error = result.data.at(0);
            // type: 1 byte; for dll item, this value is 2.
            const byte type = result.data.at(1);
            const uint pid = extractInt(data + 2);
            const uint tid = extractInt(data + 6);
            const uint codeseg = extractInt(data + 10);
            const uint dataseg = extractInt(data + 14);
            const uint len = extractShort(data + 18);
            const QByteArray name = result.data.mid(20, len); // library name
            m_session.modules += QString::fromAscii(name);
            QString logMsg;
            QTextStream str(&logMsg);
            str << prefix << " NOTE: LIBRARY LOAD: token=" << result.token;
            if (error)
                str << " ERROR: " << int(error);
            str << " TYPE: " << int(type) << " PID: " << pid << " TID:   " <<  tid;
            str << " CODE: " << hexxNumber(codeseg);
            str << " DATA: " << hexxNumber(dataseg);
            str << " NAME: '" << name << '\'';
            logMessage(logMsg);
            // This lets gdb trigger a register update etc
            //sendGdbServerMessage("T05library:r;");
            sendTrkMessage(0x18, TrkCallback(), trkContinueMessage(), "CONTINUE");
            break;
        }
        case 0xa1: { // NotifyDeleted
            const ushort itemType = extractByte(result.data.data() + 1);
            const ushort len = result.data.size() > 12
                ? extractShort(result.data.data() + 10) : ushort(0);
            const QString name = len
                ? QString::fromAscii(result.data.mid(12, len)) : QString();
            if (!name.isEmpty())
                m_session.modules.removeAll(name);
            logMessage(QString::fromLatin1("%1 %2 UNLOAD: %3")
                .arg(QString::fromAscii(prefix))
                .arg(itemType ? QLatin1String("LIB") : QLatin1String("PROCESS"))
                .arg(name));
            sendTrkAck(result.token);
            if (itemType == 0) {
                sendGdbServerMessage("W00", "Process exited");
                //sendTrkMessage(0x02, TrkCB(handleDisconnect));
            }
            break;
        }
        case 0xa2: { // NotifyProcessorStarted
            logMessage(prefix + "NOTE: PROCESSOR STARTED: " + str);
            sendTrkAck(result.token);
            break;
        }
        case 0xa6: { // NotifyProcessorStandby
            logMessage(prefix + "NOTE: PROCESSOR STANDBY: " + str);
            sendTrkAck(result.token);
            break;
        }
        case 0xa7: { // NotifyProcessorReset
            logMessage(prefix + "NOTE: PROCESSOR RESET: " + str);
            sendTrkAck(result.token);
            break;
        }
        default: {
            logMessage(prefix + "INVALID: " + str);
            break;
        }
    }
}

hjk's avatar
hjk committed
void TrkGdbAdapter::handleCpuType(const TrkResult &result)
{
    //---TRK------------------------------------------------------
    //  Command: 0x80 Acknowledge
    //    Error: 0x00
    // [80 03 00  04 00 00 04 00 00 00]
    m_session.cpuMajor = result.data[1];
    m_session.cpuMinor = result.data[2];
    m_session.bigEndian = result.data[3];
    m_session.defaultTypeSize = result.data[4];
    m_session.fpTypeSize = result.data[5];
    m_session.extended1TypeSize = result.data[6];
    //m_session.extended2TypeSize = result.data[6];
    QString logMsg;
    QTextStream(&logMsg) << "HANDLE CPU TYPE: CPU=" << m_session.cpuMajor << '.'
        << m_session.cpuMinor << " bigEndian=" << m_session.bigEndian
        << " defaultTypeSize=" << m_session.defaultTypeSize
        << " fpTypeSize=" << m_session.fpTypeSize
        << " extended1TypeSize=" <<  m_session.extended1TypeSize;
    logMessage(logMsg);
}

hjk's avatar
hjk committed
void TrkGdbAdapter::handleCreateProcess(const TrkResult &result)
{
    //  40 00 00]
    //logMessage("       RESULT: " + result.toString());
    // [80 08 00   00 00 01 B5   00 00 01 B6   78 67 40 00   00 40 00 00]
    const char *data = result.data.data();
    m_session.pid = extractInt(data + 1);
    m_session.tid = extractInt(data + 5);
    m_session.codeseg = extractInt(data + 9);
    m_session.dataseg = extractInt(data + 13);

    logMessage("PID: " + hexxNumber(m_session.pid));
    logMessage("TID: " + hexxNumber(m_session.tid));
    logMessage("COD: " + hexxNumber(m_session.codeseg));
    logMessage("DAT: " + hexxNumber(m_session.dataseg));

    QByteArray ba;
    appendInt(&ba, m_session.pid);
    appendInt(&ba, m_session.tid);

    startGdb();
}

hjk's avatar
hjk committed
void TrkGdbAdapter::handleReadRegisters(const TrkResult &result)
{
    logMessage("       RESULT: " + result.toString());
    // [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());
        return;
    }
    const char *data = result.data.data() + 1; // Skip ok byte
    for (int i = 0; i < RegisterCount; ++i)
        m_snapshot.registers[i] = extractInt(data + 4 * i);
} 

hjk's avatar
hjk committed
void TrkGdbAdapter::handleAndReportReadRegisters(const TrkResult &result)
{
    handleReadRegisters(result);
    QByteArray ba;
    for (int i = 0; i < 16; ++i) {
hjk's avatar
hjk committed
        const uint reg = swapEndian(m_snapshot.registers[i]);
        ba += 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);
}

hjk's avatar
hjk committed
static void appendRegister(QByteArray *ba, uint regno, uint value)
{
    ba->append(hexNumber(regno, 2));
    ba->append(':');
    ba->append(hexNumber(swapEndian(value), 8));
    ba->append(';');
}

void TrkGdbAdapter::handleAndReportReadRegistersAfterStop(const TrkResult &result)
{
    handleReadRegisters(result);
    QByteArray ba = "T05";
    for (int i = 0; i < 16; ++i)
        appendRegister(&ba, i, m_snapshot.registers[i]);
    //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");
}

hjk's avatar
hjk committed
static QString msgMemoryReadError(int code, uint addr, uint len = 0)
{
    const QString lenS = len ? QString::number(len) : QLatin1String("<unknown>");
    return QString::fromLatin1("Memory read error %1 at: 0x%2 %3")
        .arg(code).arg(addr, 0 ,16).arg(lenS);
}

hjk's avatar
hjk committed
void TrkGdbAdapter::handleReadMemoryBuffered(const TrkResult &result)