Commit 52b33a75 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger[Trk]: Prepare thread handling.

Store threads and cache registers per thread in Snapshot.
As Trk does not generate Thread creation/deletion events.
try to add threads on the fly if a stop in a new thread id is reported.
Remove them in continue. Continue all threads in classic Trk.
Pass on state (crash reason) to ThreadData and model.
Factor out common code of both adapters to Snapshot/Symbian classes.
parent 8254f8b2
......@@ -2761,8 +2761,8 @@ void GdbEngine::selectThread(int index)
threadsHandler()->setCurrentThread(index);
Threads threads = threadsHandler()->threads();
QTC_ASSERT(index < threads.size(), return);
int id = threads.at(index).id;
showStatusMessage(tr("Retrieving data for stack view..."), 10000);
const int id = threads.at(index).id;
showStatusMessage(tr("Retrieving data for stack view thread 0x%1...").arg(id, 0, 16), 10000);
postCommand("-thread-select " + QByteArray::number(id), CB(handleStackSelectThread));
}
......
......@@ -28,6 +28,8 @@
**************************************************************************/
#include "symbian.h"
#include "registerhandler.h"
#include "threadshandler.h"
#include <trkutils.h>
#include <utils/qtcassert.h>
......@@ -118,6 +120,68 @@ QByteArray dumpRegister(uint n, uint value)
return ba;
}
///////////////////////////////////////////////////////////////////////////
//
// Thread
//
///////////////////////////////////////////////////////////////////////////
Thread::Thread(unsigned theId) : id(theId)
{
resetRegisters();
}
void Thread::resetRegisters()
{
qFill(registers, registers + RegisterCount, uint(0));
registerValid = false;
}
QByteArray Thread::gdbReportRegisters() const
{
QByteArray ba;
for (int i = 0; i < 16; ++i) {
const uint reg = trk::swapEndian(registers[i]);
ba += trk::hexNumber(reg, 8);
}
return ba;
}
QByteArray Thread::gdbRegisterLogMessage(bool verbose) const
{
QByteArray logMsg = "REGISTER CONTENTS: (Thread 0x";
logMsg += QByteArray::number(id, 16);
logMsg += " ) ";
if (verbose) {
for (int i = 0; i < RegisterCount; ++i) {
logMsg += dumpRegister(i, registers[i]);
logMsg += ' ';
}
}
return logMsg;
}
QByteArray Thread::gdbReportSingleRegister(unsigned i) const
{
if (i == RegisterPSGdb)
i = RegisterPSTrk;
if (i >= RegisterCount)
return QByteArray("0000"); // Unknown
QByteArray ba;
appendInt(&ba, registers[i], trk::LittleEndian);
return ba.toHex();
}
QByteArray Thread::gdbSingleRegisterLogMessage(unsigned i) const
{
if (i == RegisterPSGdb)
i = RegisterPSTrk;
if (i >= RegisterCount)
return QByteArray("Read single unknown register #") + QByteArray::number(i);
QByteArray logMsg = "Read Register ";
logMsg += dumpRegister(i, registers[i]);
return logMsg;
}
///////////////////////////////////////////////////////////////////////////
//
......@@ -125,6 +189,12 @@ QByteArray dumpRegister(uint n, uint value)
//
///////////////////////////////////////////////////////////////////////////
Snapshot::Snapshot()
{
reset();
threadInfo.reserve(10);
}
void Snapshot::reset()
{
for (Memory::Iterator it = memory.begin(); it != memory.end(); ++it) {
......@@ -134,20 +204,30 @@ void Snapshot::reset()
it = memory.erase(it);
}
}
for (int i = 0; i < RegisterCount; ++i)
registers[i] = 0;
registerValid = false;
const int threadCount = threadInfo.size();
for (int i =0; i < threadCount; i++) {
threadInfo[i].resetRegisters();
threadInfo[i].state.clear();
}
wantedMemory = MemoryRange();
lineFromAddress = 0;
lineToAddress = 0;
}
void Snapshot::fullReset()
void Snapshot::resetMemory()
{
memory.clear();
reset();
}
void Snapshot::fullReset()
{
threadInfo.clear();
resetMemory();
}
void Snapshot::insertMemory(const MemoryRange &range, const QByteArray &ba)
{
QTC_ASSERT(range.size() == uint(ba.size()),
......@@ -189,15 +269,20 @@ QString Snapshot::toString() const
typedef QMap<MemoryRange, QByteArray>::const_iterator MemCacheConstIt;
QString rc;
QTextStream str(&rc);
str << "Register valid " << registerValid << ' ';
foreach(const Thread &thread, threadInfo) {
str << " Thread " << thread.id << ' ' << thread.state
<< " Register valid " << thread.registerValid << ' ';
if (thread.registerValid) {
for (int i = 0; i < RegisterCount; i++) {
if (i)
str << ", ";
str << " R" << i << "=0x";
str.setIntegerBase(16);
str << registers[i];
str << thread.registers[i];
str.setIntegerBase(10);
}
}
}
str << '\n';
// For next step.
if (!memory.isEmpty()) {
......@@ -210,6 +295,301 @@ QString Snapshot::toString() const
return rc;
}
void Snapshot::addThread(uint id)
{
if (!id || id == uint(-1)) {
qWarning("Cowardly refusing to add thread %d", id);
return;
}
const int index = indexOfThread(id);
if (index == -1) {
threadInfo.push_back(Thread(id));
} else {
threadInfo[index].resetRegisters();
qWarning("Attempt to re-add existing thread %d", id);
}
}
void Snapshot::removeThread(uint id)
{
const int index = indexOfThread(id);
if (index != -1) {
threadInfo.remove(index);
} else {
qWarning("Attempt to remove non-existing thread %d", id);
}
}
int Snapshot::indexOfThread(uint id) const
{
const int count = threadInfo.size();
for (int i = 0; i < count; i++)
if (threadInfo.at(i).id == id)
return i;
return -1;
}
uint *Snapshot::registers(uint threadId)
{
const int index = indexOfThread(threadId);
QTC_ASSERT(index != -1, { qWarning("No such thread %d", threadId); return 0; } );
return threadInfo[index].registers;
}
const uint *Snapshot::registers(uint threadId) const
{
const int index = indexOfThread(threadId);
QTC_ASSERT(index != -1, return 0; );
return threadInfo.at(index).registers;
}
uint Snapshot::registerValue(uint threadId, uint index)
{
if (const uint *regs = registers(threadId))
return regs[index];
return 0;
}
void Snapshot::setRegisterValue(uint threadId, uint index, uint value)
{
uint *regs = registers(threadId);
QTC_ASSERT(regs, return; );
regs[index] = value;
}
bool Snapshot::registersValid(uint threadId) const
{
const int index = indexOfThread(threadId);
return index != -1 ? threadInfo.at(index).registerValid : false;
}
void Snapshot::setRegistersValid(uint threadId, bool e)
{
const int index = indexOfThread(threadId);
QTC_ASSERT(index != -1, return; );
threadInfo[index].registerValid = e;
}
void Snapshot::setThreadState(uint threadId, const QString &state)
{
const int index = indexOfThread(threadId);
QTC_ASSERT(index != -1, return; );
threadInfo[index].state = state;
}
QByteArray Snapshot::gdbQsThreadInfo() const
{
// FIXME: Limit packet length by using qsThreadInfo packages ('m', ..'l')
QByteArray response(1, 'l');
const int count = threadInfo.size();
for (int i = 0; i < count; i++) {
if (i)
response += ',';
response += trk::hexNumber(threadInfo.at(i).id);
}
return response;
}
// $qThreadExtraInfo,1f9#55
QByteArray Snapshot::gdbQThreadExtraInfo(const QByteArray &cmd) const
{
const int pos = cmd.indexOf(',');
if (pos != 1) {
const uint threadId = cmd.mid(pos + 1).toUInt(0, 16);
const int threadIndex = indexOfThread(threadId);
if (threadIndex != -1 && !threadInfo.at(threadIndex).state.isEmpty())
return threadInfo.at(threadIndex).state.toAscii().toHex();
}
return QByteArray("Nothing special").toHex();
}
static void gdbAppendRegister(QByteArray *ba, uint regno, uint value)
{
ba->append(trk::hexNumber(regno, 2));
ba->append(':');
ba->append(trk::hexNumber(trk::swapEndian(value), 8));
ba->append(';');
}
QByteArray Snapshot::gdbStopMessage(uint threadId, bool reportThreadId) const
{
QByteArray ba = "T05";
if (reportThreadId) {
ba += "thread:";
ba += trk::hexNumber(threadId, 3);
ba += ';';
}
const int threadIndex = indexOfThread(threadId);
QTC_ASSERT(threadIndex != -1, return QByteArray(); );
const Thread &thread = threadInfo.at(threadIndex);
for (int i = 0; i < 16; ++i)
gdbAppendRegister(&ba, i, thread.registers[i]);
// FIXME: those are not understood by gdb 6.4
//for (int i = 16; i < 25; ++i)
// appendRegister(&ba, i, 0x0);
gdbAppendRegister(&ba, RegisterPSGdb, thread.registers[RegisterPSTrk]);
return ba;
}
// Format log message for memory access with some smartness about registers
QByteArray Snapshot::memoryReadLogMessage(uint addr, uint threadId, bool verbose, const QByteArray &ba) const
{
QByteArray logMsg = "memory contents";
const uint *regs = registers(threadId);
if (verbose && regs) {
logMsg += " addr: " + trk::hexxNumber(addr);
// indicate dereferencing of registers
if (ba.size() == 4) {
if (addr == regs[RegisterPC]) {
logMsg += "[PC]";
} else if (addr == regs[RegisterPSTrk]) {
logMsg += "[PSTrk]";
} else if (addr == regs[RegisterSP]) {
logMsg += "[SP]";
} else if (addr == regs[RegisterLR]) {
logMsg += "[LR]";
} else if (addr > regs[RegisterSP] &&
(addr - regs[RegisterSP]) < 10240) {
logMsg += "[SP+"; // Stack area ...stack seems to be top-down
logMsg += QByteArray::number(addr - regs[RegisterSP]);
logMsg += ']';
}
}
logMsg += " length ";
logMsg += QByteArray::number(ba.size());
logMsg += " :";
logMsg += trk::stringFromArray(ba, 16).toAscii();
}
return logMsg;
}
void Snapshot::syncRegisters(uint threadId, RegisterHandler *handler) const
{
// Take advantage of direct access to cached register values.
const int threadIndex = indexOfThread(threadId);
QTC_ASSERT(threadIndex != -1, return ;);
const Thread &thread = threadInfo.at(threadIndex);
QTC_ASSERT(thread.registerValid, return ;);
Registers debuggerRegisters = handler->registers();
QTC_ASSERT(debuggerRegisters.size() >= RegisterPSGdb,
qDebug() << "HAVE: " << debuggerRegisters.size(); return);
bool changed = false;
for (int i = 0; i < RegisterCount; ++i) {
const int gdbIndex = i == RegisterPSTrk ? int(RegisterPSGdb) : i;
Register &reg = debuggerRegisters[gdbIndex];
const QString value = trk::hexxNumber(thread.registers[i]);
reg.changed = (value != reg.value);
if (reg.changed) {
reg.value = value;
changed = true;
}
}
if (changed)
handler->setRegisters(debuggerRegisters);
}
void Snapshot::parseGdbStepRange(const QByteArray &cmd, bool so)
{
const int pos = cmd.indexOf(',', 8);
lineFromAddress = cmd.mid(8, pos - 8).toUInt(0, 16);
lineToAddress = cmd.mid(pos + 1).toUInt(0, 16);
stepOver = so;
}
void Snapshot::syncThreads(ThreadsHandler *handler) const
{
// Take advantage of direct access to cached register values.
Threads threads;
const unsigned count = threadInfo.size();
for (unsigned t = 0; t < count; t++) {
ThreadData thread(t + 1); // Fake gdb thread ids starting from 1
thread.targetId = QString::number(threadInfo.at(t).id);
thread.state = threadInfo.at(t).state;
threads.append(thread);
}
handler->setThreads(threads);
}
// Answer to gdb's 'qSupported' query:
// Increase buffer size for qXfer::libraries XML response
const char *gdbQSupported =
"PacketSize=20000;"
"QPassSignals+;"
"QStartNoAckMode+;"
"qXfer:libraries:read+;"
// "qXfer:auxv:read+;"
"qXfer:features:read+";
// Answer to gdb "qXfer:features:read:target.xml:" request
// "l<target><architecture>symbianelf</architecture></target>"
// "l<target><architecture>arm-none-symbianelf</architecture></target>"
const char *gdbArchitectureXml = "l<target><architecture>arm</architecture></target>";
QVector<QByteArray> gdbStartupSequence()
{
QVector<QByteArray> s;
s.reserve(10);
s.push_back(QByteArray("set breakpoint always-inserted on"));
s.push_back(QByteArray("set breakpoint auto-hw on"));
s.push_back(QByteArray("set trust-readonly-sections on")); // No difference?
s.push_back(QByteArray("set displaced-stepping on")); // No difference?
s.push_back(QByteArray("set mem inaccessible-by-default"));
s.push_back(QByteArray("mem 0x00400000 0x70000000 cache"));
s.push_back(QByteArray("mem 0x70000000 0x80000000 cache ro"));
// FIXME: replace with stack-cache for newer gdb?
s.push_back(QByteArray("set remotecache on")); // "info dcache" to check
return s;
}
} // namespace Symbian
// Generic gdb server helpers: Read address/length off a memory
// command like 'm845,455','X845,455'
QPair<quint64, unsigned> parseGdbReadMemoryRequest(const QByteArray &cmd)
{
QPair<quint64, unsigned> rc(0, 0);
const int pos = cmd.indexOf(',');
if (pos == -1)
return rc;
bool ok;
rc.first = cmd.mid(1, pos - 1).toULongLong(&ok, 16);
if (!ok)
return rc;
rc.second = cmd.mid(pos + 1).toUInt(&ok, 16);
if (!ok)
rc.first = 0;
return rc;
}
// Generic gdb server helpers: Parse 'register write' ('P') request
// return register number/value
QPair<uint, uint> parseGdbWriteRegisterWriteRequest(const QByteArray &cmd)
{
const int pos = cmd.indexOf('=');
const QByteArray regName = cmd.mid(1, pos - 1);
const QByteArray valueName = cmd.mid(pos + 1);
bool ok = false;
const uint registerNumber = regName.toUInt(&ok, 16);
const uint value = trk::swapEndian(valueName.toUInt(&ok, 16));
return QPair<uint, uint>(registerNumber, value);
}
// Generic gdb server helpers: Parse 'set breakpoint' ('Z0') request
// return address/length
QPair<quint64, unsigned> parseGdbSetBreakpointRequest(const QByteArray &cmd)
{
// $Z0,786a4ccc,4#99
const int pos = cmd.lastIndexOf(',');
bool ok1 = false;
bool ok2 = false;
const quint64 addr = cmd.mid(3, pos - 3).toULongLong(&ok1, 16);
const uint len = cmd.mid(pos + 1).toUInt(&ok2, 16);
return ok1 && ok2 ? QPair<quint64, unsigned>(addr, len) : QPair<quint64, unsigned>(0, 0);
}
} // namespace Internal
} // namespace Debugger
......@@ -33,6 +33,8 @@
#include <QtCore/QMap>
#include <QtCore/QByteArray>
#include <QtCore/QMetaType>
#include <QtCore/QVector>
#include <QtCore/QPair>
QT_BEGIN_NAMESPACE
class QDebug;
......@@ -48,7 +50,8 @@ QT_END_NAMESPACE
namespace Debugger {
namespace Internal {
class RegisterHandler;
class ThreadsHandler;
struct GdbResult {
QByteArray data;
};
......@@ -100,17 +103,62 @@ inline bool isReadOnly(const MemoryRange &mr)
return mr.from >= 0x70000000 && mr.to < 0x80000000;
}
// Snapshot thread with cached registers
struct Thread {
explicit Thread(unsigned id = 0);
void resetRegisters();
// Gdb helpers for reporting values
QByteArray gdbReportRegisters() const;
QByteArray gdbRegisterLogMessage(bool verbose) const;
QByteArray gdbReportSingleRegister(unsigned i) const;
QByteArray gdbSingleRegisterLogMessage(unsigned i) const;
uint id;
uint registers[RegisterCount];
bool registerValid;
QString state; // Stop reason, for qsThreadExtraInfo
};
struct Snapshot
{
Snapshot() { reset(); }
Snapshot();
void reset(); // Leaves read-only memory cache alive.
void fullReset(); // Also removes read-only memory cache.
void reset(); // Leaves read-only memory cache and threads alive.
void resetMemory(); // Completely clears memory, leaves threads alive.
void fullReset(); // Clear everything.
void insertMemory(const MemoryRange &range, const QByteArray &ba);
QString toString() const;
uint registers[RegisterCount];
bool registerValid;
// Helpers to format gdb query packets
QByteArray gdbQsThreadInfo() const;
QByteArray gdbQThreadExtraInfo(const QByteArray &cmd) const;
// Format a gdb T05 stop message with thread and register set
QByteArray gdbStopMessage(uint threadId, bool reportThreadId) const;
// Format a log message for memory access with some smartness about registers
QByteArray memoryReadLogMessage(uint addr, uint threadId, bool verbose, const QByteArray &ba) const;
// Gdb command parse helpers: 'salnext'
void parseGdbStepRange(const QByteArray &cmd, bool stepOver);
void addThread(uint threadId);
void removeThread(uint threadId);
int indexOfThread(uint threadId) const;
// Access registers by thread
const uint *registers(uint threadId) const;
uint *registers(uint threadId);
uint registerValue(uint threadId, uint index);
void setRegisterValue(uint threadId, uint index, uint value);
bool registersValid(uint threadId) const;
void setRegistersValid(uint threadId, bool e);
void setThreadState(uint threadId, const QString&);
// Debugger view helpers: Synchronize registers of thread with register handler.
void syncRegisters(uint threadId, RegisterHandler *handler) const;
// Debugger view helpers: Synchronize threads with threads handler.
void syncThreads(ThreadsHandler *handler) const;
QVector<Thread> threadInfo;
typedef QMap<MemoryRange, QByteArray> Memory;
Memory memory;
......@@ -136,7 +184,21 @@ struct Breakpoint
CodeMode mode;
};
// Gdb helpers
extern const char *gdbQSupported;
extern const char *gdbArchitectureXml;
QVector<QByteArray> gdbStartupSequence();
} // namespace Symbian
// Generic gdb server helpers: read 'm','X' commands.
QPair<quint64, unsigned> parseGdbReadMemoryRequest(const QByteArray &cmd);
// Parse 'register write' ('P') request, return register number/value
QPair<uint, uint> parseGdbWriteRegisterWriteRequest(const QByteArray &cmd);
// Parse 'set breakpoint' ('Z0') request, return address/length
QPair<quint64, unsigned> parseGdbSetBreakpointRequest(const QByteArray &cmd);
} // namespace Internal
} // namespace Debugger
......
......@@ -116,15 +116,15 @@ private:
void handleAndReportReadRegistersAfterStop(const tcftrk::TcfTrkCommandResult &result);
void handleAndReportSetBreakpoint(const tcftrk::TcfTrkCommandResult &result);
void handleClearBreakpoint(const tcftrk::TcfTrkCommandResult &result);
void handleSignalContinue(const tcftrk::TcfTrkCommandResult &result);
void readMemory(uint addr, uint len, bool buffered);
void handleReadMemoryBuffered(const tcftrk::TcfTrkCommandResult &result);
void handleReadMemoryUnbuffered(const tcftrk::TcfTrkCommandResult &result);
void handleWriteMemory(const tcftrk::TcfTrkCommandResult &result);
void tryAnswerGdbMemoryRequest(bool buffered);
inline void sendMemoryGetCommand(const MemoryRange &range, bool buffered);
void addThread(unsigned id);
inline QByteArray mainThreadContextId() const;
QByteArray memoryReadLogMessage(uint addr, const QByteArray &ba) const;
inline QByteArray currentThreadContextId() const;
AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
......@@ -157,6 +157,7 @@ private:
const QByteArray &logNote = QByteArray());
void sendGdbServerAck();
bool sendGdbServerPacket(const QByteArray &packet, bool doFlush);
void gdbSetCurrentThread(const QByteArray &cmd, const char *why);
void logMessage(const QString &msg, int channel = LogDebug); // triggers output() if m_verbose