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,14 +269,19 @@ QString Snapshot::toString() const
typedef QMap<MemoryRange, QByteArray>::const_iterator MemCacheConstIt;
QString rc;
QTextStream str(&rc);
str << "Register valid " << registerValid << ' ';
for (int i = 0; i < RegisterCount; i++) {
if (i)
str << ", ";
str << " R" << i << "=0x";
str.setIntegerBase(16);
str << registers[i];
str.setIntegerBase(10);
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 << thread.registers[i];
str.setIntegerBase(10);
}
}
}
str << '\n';
// For next step.
......@@ -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
......
......@@ -62,14 +62,6 @@
enum { debug = 0 };
static void appendRegister(QByteArray *ba, uint regno, uint value)
{
ba->append(trk::hexNumber(regno, 2));
ba->append(':');
ba->append(trk::hexNumber(trk::swapEndian(value), 8));
ba->append(';');
}
// Register names used by the 'SimpleRegister' service
static const char* tcfTrkSimpleRegisterNamesC[] =
{"R0", "R1", "R2", "R3",
......@@ -100,7 +92,15 @@ static inline QString startMsg(const trk::Session &session)
* 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