1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
34 #include "registerhandler.h"
35 #include "threadshandler.h"
38 #include <utils/qtcassert.h>
40 #include <QtCore/QDebug>
41 #include <QtCore/QTextStream>
42 #include <QtCore/QFileInfo>
47 ///////////////////////////////////////////////////////////////////////////
51 ///////////////////////////////////////////////////////////////////////////
53 MemoryRange::MemoryRange(uint f, uint t)
56 QTC_ASSERT(f <= t, qDebug() << "F: " << f << " T: " << t);
59 bool MemoryRange::intersects(const MemoryRange &other) const
62 QTC_ASSERT(false, /**/);
63 return false; // FIXME
66 void MemoryRange::operator-=(const MemoryRange &other)
68 if (from == 0 && to == 0)
70 MEMORY_DEBUG(" SUB: " << *this << " - " << other);
71 if (other.from <= from && to <= other.to) {
75 if (other.from <= from && other.to <= to) {
76 from = qMax(from, other.to);
79 if (from <= other.from && to <= other.to) {
80 to = qMin(other.from, to);
83 // This would split the range.
84 QTC_ASSERT(false, qDebug() << "Memory::operator-() not handled for: "
85 << *this << " - " << other);
88 QDebug operator<<(QDebug d, const MemoryRange &range)
90 return d << QString("[%1,%2] (size %3) ")
91 .arg(range.from, 0, 16).arg(range.to, 0, 16).arg(range.size());
96 static const char *registerNames[KnownRegisters] =
98 "A1", "A2", "A3", "A4",
101 "IP", "SP", "LR", "PC",
107 const char *registerName(int i)
109 return registerNames[i];
112 QByteArray dumpRegister(uint n, uint value)
116 if (n < KnownRegisters && registerNames[n]) {
117 ba += registerNames[n];
120 ba += QByteArray::number(n);
123 ba += trk::hexxNumber(value);
127 ///////////////////////////////////////////////////////////////////////////
131 ///////////////////////////////////////////////////////////////////////////
133 Thread::Thread(unsigned theId) : id(theId)
138 void Thread::resetRegisters()
140 qFill(registers, registers + RegisterCount, uint(0));
141 registerValid = false;
144 QByteArray Thread::gdbReportRegisters() const
147 for (int i = 0; i < 16; ++i) {
148 const uint reg = trk::swapEndian(registers[i]);
149 ba += trk::hexNumber(reg, 8);
154 QByteArray Thread::registerContentsLogMessage() const
157 for (int i = 0; i < RegisterCount; ++i) {
158 logMsg += dumpRegister(i, registers[i]);
164 QByteArray Thread::gdbRegisterLogMessage(bool verbose) const
166 QByteArray logMsg = "Register contents: (Thread 0x";
167 logMsg += QByteArray::number(id, 16);
170 logMsg += registerContentsLogMessage();
174 QByteArray Thread::gdbReportSingleRegister(unsigned i) const
176 if (i == RegisterPSGdb)
178 if (i >= RegisterCount)
179 return QByteArray("0000"); // Unknown
181 appendInt(&ba, registers[i], trk::LittleEndian);
185 QByteArray Thread::gdbSingleRegisterLogMessage(unsigned i) const
187 if (i == RegisterPSGdb)
189 if (i >= RegisterCount)
190 return QByteArray("Read single unknown register #") + QByteArray::number(i);
191 QByteArray logMsg = "Read Register ";
192 logMsg += dumpRegister(i, registers[i]);
196 ///////////////////////////////////////////////////////////////////////////
200 ///////////////////////////////////////////////////////////////////////////
205 threadInfo.reserve(10);
208 void Snapshot::reset()
210 MEMORY_DEBUG("RESET SNAPSHOT MEMORY INITIALLY: " << memory.size() << " BLOCK LEFT");
211 for (Memory::Iterator it = memory.begin(); it != memory.end(); ) {
212 MEMORY_DEBUG("EXAMINING " << it.key());
213 it = memory.erase(it);
215 MEMORY_DEBUG("RESET SNAPSHOT MEMORY FINALLY: " << memory.size() << " BLOCK LEFT");
217 const int threadCount = threadInfo.size();
218 for (int i =0; i < threadCount; i++) {
219 threadInfo[i].resetRegisters();
220 threadInfo[i].state.clear();
223 wantedMemory = MemoryRange();
228 void Snapshot::resetMemory()
234 void Snapshot::fullReset()
240 void Snapshot::insertMemory(const MemoryRange &range, const QByteArray &ba)
242 QTC_ASSERT(range.size() == uint(ba.size()),
243 qDebug() << "RANGE: " << range << " BA SIZE: " << ba.size(); return);
245 MEMORY_DEBUG("INSERT: " << range);
246 // Try to combine with existing chunk.
247 Snapshot::Memory::iterator it = memory.begin();
248 Snapshot::Memory::iterator et = memory.end();
249 for ( ; it != et; ++it) {
250 if (range.from == it.key().to) {
251 MEMORY_DEBUG("COMBINING " << it.key() << " AND " << range);
252 QByteArray data = *it;
254 const MemoryRange res(it.key().from, range.to);
255 memory.remove(it.key());
256 MEMORY_DEBUG(" TO(1) " << res);
257 insertMemory(res, data);
260 if (it.key().from == range.to) {
261 MEMORY_DEBUG("COMBINING " << range << " AND " << it.key());
262 QByteArray data = ba;
264 const MemoryRange res(range.from, it.key().to);
265 memory.remove(it.key());
266 MEMORY_DEBUG(" TO(2) " << res);
267 insertMemory(res, data);
272 // Not combinable, add chunk.
273 memory.insert(range, ba);
276 QString Snapshot::toString() const
278 typedef QMap<MemoryRange, QByteArray>::const_iterator MemCacheConstIt;
280 QTextStream str(&rc);
281 foreach(const Thread &thread, threadInfo) {
282 str << " Thread " << thread.id << ' ' << thread.state
283 << " Register valid " << thread.registerValid << ' ';
284 if (thread.registerValid) {
285 for (int i = 0; i < RegisterCount; i++) {
288 str << " R" << i << "=0x";
289 str.setIntegerBase(16);
290 str << thread.registers[i];
291 str.setIntegerBase(10);
297 if (!memory.isEmpty()) {
298 str.setIntegerBase(16);
300 const MemCacheConstIt mcend = memory.constEnd();
301 for (MemCacheConstIt it = memory.constBegin(); it != mcend; ++it)
302 str << " 0x" << it.key().from << " - 0x" << it.key().to << '\n';
307 void Snapshot::addThread(uint id)
309 if (!id || id == uint(-1)) {
310 qWarning("Cowardly refusing to add thread %d", id);
314 const int index = indexOfThread(id);
316 threadInfo.push_back(Thread(id));
318 threadInfo[index].resetRegisters();
319 qWarning("Attempt to re-add existing thread %d", id);
323 void Snapshot::removeThread(uint id)
325 const int index = indexOfThread(id);
327 threadInfo.remove(index);
329 qWarning("Attempt to remove non-existing thread %d", id);
333 int Snapshot::indexOfThread(uint id) const
335 const int count = threadInfo.size();
336 for (int i = 0; i < count; i++)
337 if (threadInfo.at(i).id == id)
342 uint *Snapshot::registers(uint threadId)
344 const int index = indexOfThread(threadId);
345 QTC_ASSERT(index != -1, { qWarning("No such thread %d", threadId); return 0; } );
346 return threadInfo[index].registers;
349 const uint *Snapshot::registers(uint threadId) const
351 const int index = indexOfThread(threadId);
352 QTC_ASSERT(index != -1, return 0; );
353 return threadInfo.at(index).registers;
356 uint Snapshot::registerValue(uint threadId, uint index)
358 if (const uint *regs = registers(threadId))
363 void Snapshot::setRegisterValue(uint threadId, uint index, uint value)
365 uint *regs = registers(threadId);
366 QTC_ASSERT(regs, return; );
370 bool Snapshot::registersValid(uint threadId) const
372 const int index = indexOfThread(threadId);
373 return index != -1 ? threadInfo.at(index).registerValid : false;
376 void Snapshot::setRegistersValid(uint threadId, bool e)
378 const int index = indexOfThread(threadId);
379 QTC_ASSERT(index != -1, return; );
380 threadInfo[index].registerValid = e;
383 void Snapshot::setThreadState(uint threadId, const QString &state)
385 const int index = indexOfThread(threadId);
386 QTC_ASSERT(index != -1, return; );
387 threadInfo[index].state = state;
390 QByteArray Snapshot::gdbQsThreadInfo() const
392 // FIXME: Limit packet length by using qsThreadInfo packages ('m', ..'l')
393 QByteArray response(1, 'l');
394 const int count = threadInfo.size();
395 for (int i = 0; i < count; i++) {
398 response += trk::hexNumber(threadInfo.at(i).id);
403 // $qThreadExtraInfo,1f9#55
404 QByteArray Snapshot::gdbQThreadExtraInfo(const QByteArray &cmd) const
406 const int pos = cmd.indexOf(',');
408 const uint threadId = cmd.mid(pos + 1).toUInt(0, 16);
409 const int threadIndex = indexOfThread(threadId);
410 if (threadIndex != -1 && !threadInfo.at(threadIndex).state.isEmpty())
411 return threadInfo.at(threadIndex).state.toAscii().toHex();
413 return QByteArray("Nothing special").toHex();
416 static void gdbAppendRegister(QByteArray *ba, uint regno, uint value)
418 ba->append(trk::hexNumber(regno, 2));
420 ba->append(trk::hexNumber(trk::swapEndian(value), 8));
424 QByteArray Snapshot::gdbStopMessage(uint threadId, int signalNumber, bool reportThreadId) const
426 QByteArray ba = ('T' + trk::hexNumber(signalNumber, 2));
427 if (reportThreadId) {
429 ba += trk::hexNumber(threadId, 3);
432 const int threadIndex = indexOfThread(threadId);
433 QTC_ASSERT(threadIndex != -1, return QByteArray(); );
434 const Thread &thread = threadInfo.at(threadIndex);
435 for (int i = 0; i < 16; ++i)
436 gdbAppendRegister(&ba, i, thread.registers[i]);
437 // FIXME: those are not understood by gdb 6.4
438 //for (int i = 16; i < 25; ++i)
439 // appendRegister(&ba, i, 0x0);
440 gdbAppendRegister(&ba, RegisterPSGdb, thread.registers[RegisterPSTrk]);
444 // Format log message for memory access with some smartness about registers
445 QByteArray Snapshot::memoryReadLogMessage(uint addr, uint threadId, bool verbose, const QByteArray &ba) const
447 QByteArray logMsg = "memory contents";
448 const uint *regs = registers(threadId);
449 if (verbose && regs) {
450 logMsg += " addr: " + trk::hexxNumber(addr);
451 // indicate dereferencing of registers
452 if (ba.size() == 4) {
453 if (addr == regs[RegisterPC]) {
455 } else if (addr == regs[RegisterPSTrk]) {
457 } else if (addr == regs[RegisterSP]) {
459 } else if (addr == regs[RegisterLR]) {
461 } else if (addr > regs[RegisterSP] &&
462 (addr - regs[RegisterSP]) < 10240) {
463 logMsg += "[SP+"; // Stack area ...stack seems to be top-down
464 logMsg += QByteArray::number(addr - regs[RegisterSP]);
468 logMsg += " length ";
469 logMsg += QByteArray::number(ba.size());
471 logMsg += trk::stringFromArray(ba, ba.size()).toAscii();
476 void Snapshot::syncRegisters(uint threadId, RegisterHandler *handler) const
478 // Take advantage of direct access to cached register values.
479 const int threadIndex = indexOfThread(threadId);
480 QTC_ASSERT(threadIndex != -1, return ;);
481 const Thread &thread = threadInfo.at(threadIndex);
482 QTC_ASSERT(thread.registerValid, return ;);
484 Registers debuggerRegisters = handler->registers();
485 QTC_ASSERT(debuggerRegisters.size() >= RegisterPSGdb,
486 qDebug() << "HAVE: " << debuggerRegisters.size(); return);
488 for (int i = 0; i < RegisterCount; ++i) {
489 const int gdbIndex = i == RegisterPSTrk ? int(RegisterPSGdb) : i;
490 Register ® = debuggerRegisters[gdbIndex];
491 reg.value = trk::hexxNumber(thread.registers[i]);
493 handler->setAndMarkRegisters(debuggerRegisters);
496 void Snapshot::parseGdbStepRange(const QByteArray &cmd, bool so)
498 const int pos = cmd.indexOf(',', 8);
499 lineFromAddress = cmd.mid(8, pos - 8).toUInt(0, 16);
500 lineToAddress = cmd.mid(pos + 1).toUInt(0, 16);
504 void Snapshot::syncThreads(ThreadsHandler *handler) const
506 // Take advantage of direct access to cached register values.
508 const unsigned count = threadInfo.size();
509 for (unsigned t = 0; t < count; t++) {
510 ThreadData thread(t + 1); // Fake gdb thread ids starting from 1
511 thread.targetId = QString::number(threadInfo.at(t).id);
512 thread.state = threadInfo.at(t).state;
513 threads.append(thread);
515 handler->setThreads(threads);
518 // Answer to gdb's 'qSupported' query:
519 // Increase buffer size for qXfer::libraries XML response
520 const char *gdbQSupported =
524 "qXfer:libraries:read+;"
525 // "qXfer:auxv:read+;"
526 "qXfer:features:read+;"
527 "qRelocInsn-"; // Relocate instructions for trace (gdb 7.2+): Not supported.
529 // Answer to gdb "qXfer:features:read:target.xml:" request
530 // "l<target><architecture>symbianelf</architecture></target>"
531 // "l<target><architecture>arm-none-symbianelf</architecture></target>"
533 const char *gdbArchitectureXml = "l<target><architecture>arm</architecture></target>";
535 QVector<QByteArray> gdbStartupSequence()
537 QVector<QByteArray> s;
539 s.push_back(QByteArray("set breakpoint always-inserted on"));
540 s.push_back(QByteArray("set breakpoint auto-hw on"));
541 s.push_back(QByteArray("set trust-readonly-sections on")); // No difference?
542 s.push_back(QByteArray("set displaced-stepping on")); // No difference?
543 s.push_back(QByteArray("set mem inaccessible-by-default"));
544 s.push_back(QByteArray("mem 0x00400000 0x70000000 cache"));
545 s.push_back(QByteArray("mem 0x70000000 0x80000000 cache ro"));
546 // FIXME: replace with stack-cache for newer gdb?
547 s.push_back(QByteArray("set remotecache on")); // "info dcache" to check
551 // Local symbol file handling
553 enum { symDebug = 0 };
555 // Build complete file name of a local sym file from DLL
556 // 'QtCore.dll' to 'c:\\foo\QtCore.dll.sym'.
558 static inline QString symFileName(const QString &folder,
559 const QString &libName)
561 QString fileName = folder;
562 fileName.append(QLatin1Char('/'));
563 fileName.append(libName);
564 fileName.append(QLatin1String(".sym"));
568 // Look up in local symbol file matching remote library loaded in
569 // cache pointed to by environmentname or in standard location
570 // (next to application.sym file).
571 QString localSymFileForLibrary(const QByteArray &libName,
572 // urel/udeb: exe directory
573 const QString &standardSymDirectory)
576 const QByteArray envSymFileCacheDirectory = qgetenv("QTC_SYMBIAN_SYMBOLFILE_CACHE");
577 if (envSymFileCacheDirectory.isEmpty() && standardSymDirectory.isEmpty())
580 int lastSlashPos = libName.lastIndexOf('/');
581 if (lastSlashPos == -1)
582 lastSlashPos = libName.lastIndexOf('\\');
583 const QString libBaseName = QString::fromLatin1(lastSlashPos != - 1 ? libName.mid(lastSlashPos + 1) : libName);
584 // Check environment variable
585 if (!envSymFileCacheDirectory.isEmpty()) {
586 const QFileInfo envFi(symFileName(QString::fromLatin1(envSymFileCacheDirectory), libBaseName));
588 qDebug("SYM-ENV: %s exists %d\n", qPrintable(envFi.absoluteFilePath()), envFi.isFile());
590 return envFi.absoluteFilePath();
592 // Check standard location
593 if (!standardSymDirectory.isEmpty()) {
594 const QFileInfo standardFi(symFileName(standardSymDirectory, libBaseName));
596 qDebug("SYM-STANDARD: %s exists %d\n", qPrintable(standardFi.absoluteFilePath()), standardFi.isFile());
597 if (standardFi.isFile())
598 return standardFi.absoluteFilePath();
603 // Return a load command for a local symbol file for a library with address.
604 QByteArray symFileLoadCommand(const QString &symFileNameIn,
605 quint64 code, quint64 data)
607 QByteArray symFileName = symFileNameIn.toLatin1();
608 symFileName.replace('\\', '/'); // gdb wants forward slashes
609 QByteArray command = "add-symbol-file \"";
610 command += symFileName;
612 command += QByteArray::number(code, 16);
614 command += " -s .data 0x";
615 command += QByteArray::number(data, 16);
620 QString msgLoadLocalSymFile(const QString &symFileName,
621 const QByteArray &libName, quint64 code)
623 return QString::fromAscii("Loading symbol file '%1' for '%2' at 0x%3").
624 arg(symFileName, QString::fromLatin1(libName)).
628 } // namespace Symbian
630 // Generic gdb server helpers: Read address/length off a memory
631 // command like 'm845,455','X845,455:'
632 QPair<quint64, unsigned> parseGdbReadMemoryRequest(const QByteArray &cmd)
634 QPair<quint64, unsigned> rc(0, 0);
635 const int pos = cmd.indexOf(',');
639 rc.first = cmd.mid(1, pos - 1).toULongLong(&ok, 16);
642 const int colonPos = cmd.indexOf(':');
644 rc.second = cmd.mid(pos + 1).toUInt(&ok, 16);
646 rc.second = cmd.mid(pos + 1, colonPos - pos - 1 ).toUInt(&ok, 16);
652 // Generic gdb server helpers: Parse 'register write' ('P') request
653 // return register number/value
654 QPair<uint, uint> parseGdbWriteRegisterWriteRequest(const QByteArray &cmd)
656 const int pos = cmd.indexOf('=');
657 const QByteArray regName = cmd.mid(1, pos - 1);
658 const QByteArray valueName = cmd.mid(pos + 1);
660 const uint registerNumber = regName.toUInt(&ok, 16);
661 const uint value = trk::swapEndian(valueName.toUInt(&ok, 16));
662 return QPair<uint, uint>(registerNumber, value);
665 // Generic gdb server helpers: Parse 'set breakpoint' ('Z0') request
666 // return address/length
667 QPair<quint64, unsigned> parseGdbSetBreakpointRequest(const QByteArray &cmd)
670 const int pos = cmd.lastIndexOf(',');
673 const quint64 addr = cmd.mid(3, pos - 3).toULongLong(&ok1, 16);
674 const uint len = cmd.mid(pos + 1).toUInt(&ok2, 16);
675 return ok1 && ok2 ? QPair<quint64, unsigned>(addr, len) : QPair<quint64, unsigned>(0, 0);
678 } // namespace Internal
679 } // namespace Debugger