Commit 3743211e authored by hjk's avatar hjk

Debugger: Rework register handling

Use register names as handle, not their index in the view.
Store the raw real values, not some stringified version
as primary data.  Use subentries to break down bigger
registers into smaller entities. Also remember the
previous value of a register and show it in a tooltip.

Change-Id: I8ae3cc8766a7b211bc7cc827c734e5cf6060825c
Reviewed-by: default avatarChristian Stenger <christian.stenger@theqtcompany.com>
Reviewed-by: default avatarhjk <hjk121@nokiamail.com>
parent b2bb7ea2
......@@ -1193,6 +1193,7 @@ class Dumper(DumperBase):
for reg in group:
result += '{name="%s"' % reg.GetName()
result += ',value="%s"' % reg.GetValue()
result += ',size="%s"' % reg.GetByteSize()
result += ',type="%s"},' % reg.GetType()
result += ']'
self.report(result)
......
......@@ -364,6 +364,34 @@ const wchar_t *valueType(ULONG type)
return L"";
}
// Description of a DEBUG_VALUE type field
const int valueSize(ULONG type)
{
switch (type) {
case DEBUG_VALUE_INT8:
return 1;
case DEBUG_VALUE_INT16:
return 2;
case DEBUG_VALUE_INT32:
return 4;
case DEBUG_VALUE_INT64:
return 8;
case DEBUG_VALUE_FLOAT32:
return 4;
case DEBUG_VALUE_FLOAT64:
return 8;
case DEBUG_VALUE_FLOAT80:
return 10;
case DEBUG_VALUE_FLOAT128:
return 16;
case DEBUG_VALUE_VECTOR64:
return 8;
case DEBUG_VALUE_VECTOR128:
return 16;
}
return 0;
}
// Format a 128bit vector register by adding digits in reverse order
void formatVectorRegister(std::ostream &str, const unsigned char *array, int size)
{
......@@ -421,6 +449,7 @@ void formatDebugValue(std::ostream &str, const DEBUG_VALUE &dv, CIDebugControl *
Register::Register() : subRegister(false), pseudoRegister(false)
{
size = 0;
value.Type = DEBUG_VALUE_INT32;
value.I32 = 0;
}
......@@ -497,6 +526,7 @@ Registers getRegisters(CIDebugRegisters *regs,
reg.pseudoRegister = true;
reg.name = buf;
reg.description = valueType(type);
reg.size = valueSize(type);
reg.value = value;
rc.push_back(reg);
}
......
......@@ -129,6 +129,7 @@ struct Register
std::wstring name;
std::wstring description;
int size;
bool subRegister;
bool pseudoRegister;
DEBUG_VALUE value;
......
......@@ -1235,14 +1235,12 @@ void CdbEngine::executeRunToFunction(const QString &functionName)
continueInferior();
}
void CdbEngine::setRegisterValue(int regnr, const QString &value)
void CdbEngine::setRegisterValue(const QByteArray &name, const QString &value)
{
const Registers registers = registerHandler()->registers();
QTC_ASSERT(regnr < registers.size(), return);
// Value is decimal or 0x-hex-prefixed
QByteArray cmd;
ByteArrayInputStream str(cmd);
str << "r " << registers.at(regnr).name << '=' << value;
str << "r " << name << '=' << value;
postCommand(cmd, 0);
reloadRegisters();
}
......@@ -1854,21 +1852,6 @@ void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
}
}
// Parse CDB gdbmi register syntax.
static Register parseRegister(const GdbMi &gdbmiReg)
{
Register reg;
reg.name = gdbmiReg["name"].data();
const GdbMi description = gdbmiReg["description"];
if (description.type() != GdbMi::Invalid) {
reg.name += " (";
reg.name += description.data();
reg.name += ')';
}
reg.value = gdbmiReg["value"].data();
return reg;
}
void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
{
if (reply->success) {
......@@ -1906,11 +1889,17 @@ void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
GdbMi value;
value.fromString(reply->reply);
if (value.type() == GdbMi::List) {
Registers registers;
registers.reserve(value.childCount());
foreach (const GdbMi &gdbmiReg, value.children())
registers.push_back(parseRegister(gdbmiReg));
registerHandler()->setAndMarkRegisters(registers);
RegisterHandler *handler = registerHandler();
foreach (const GdbMi &item, value.children()) {
Register reg;
reg.name = item["name"].data();
reg.description = item["description"].data();
reg.reportedType = item["type"].data();
reg.value = item["value"].data();
reg.size = item["size"].data().toInt();
handler->updateRegister(reg);
}
handler->commitUpdates();
} else {
showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
qWarning("Parse error in registers response:\n%s", reply->reply.constData());
......
......@@ -90,7 +90,7 @@ public:
const WatchUpdateFlags & flags = WatchUpdateFlags());
virtual bool hasCapability(unsigned cap) const;
virtual void watchPoint(const QPoint &);
virtual void setRegisterValue(int regnr, const QString &value);
virtual void setRegisterValue(const QByteArray &name, const QString &value);
virtual void executeStep();
virtual void executeStepOut();
......
......@@ -477,9 +477,9 @@ void DebuggerEngine::changeMemory(MemoryAgent *, QObject *,
Q_UNUSED(data);
}
void DebuggerEngine::setRegisterValue(int regnr, const QString &value)
void DebuggerEngine::setRegisterValue(const QByteArray &name, const QString &value)
{
Q_UNUSED(regnr);
Q_UNUSED(name);
Q_UNUSED(value);
}
......@@ -1746,11 +1746,9 @@ void DebuggerEngine::showStoppedByExceptionMessageBox(const QString &description
Core::AsynchronousMessageBox::information(tr("Exception Triggered"), msg);
}
void DebuggerEngine::openMemoryView(quint64 startAddr, unsigned flags,
const QList<MemoryMarkup> &ml, const QPoint &pos,
const QString &title, QWidget *parent)
void DebuggerEngine::openMemoryView(const MemoryViewSetupData &data)
{
d->m_memoryAgent.createBinEditor(startAddr, flags, ml, pos, title, parent);
d->m_memoryAgent.createBinEditor(data);
}
void DebuggerEngine::updateMemoryViews()
......
......@@ -75,7 +75,7 @@ class BreakpointParameters;
class QmlAdapter;
class QmlCppEngine;
class DebuggerToolTipContext;
class MemoryMarkup;
class MemoryViewSetupData;
struct WatchUpdateFlags
{
......@@ -158,10 +158,7 @@ public:
MemoryView = 0x4 //!< Open a separate view (using the pos-parameter).
};
virtual void openMemoryView(quint64 startAddr, unsigned flags,
const QList<Internal::MemoryMarkup> &ml,
const QPoint &pos,
const QString &title = QString(), QWidget *parent = 0);
virtual void openMemoryView(const MemoryViewSetupData &data);
virtual void fetchMemory(Internal::MemoryAgent *, QObject *,
quint64 addr, quint64 length);
virtual void changeMemory(Internal::MemoryAgent *, QObject *,
......@@ -185,7 +182,7 @@ public:
virtual void loadAdditionalQmlStack();
virtual void reloadDebuggingHelpers();
virtual void setRegisterValue(int regnr, const QString &value);
virtual void setRegisterValue(const QByteArray &name, const QString &value);
virtual void addOptionPages(QList<Core::IOptionsPage*> *) const;
virtual bool hasCapability(unsigned cap) const = 0;
virtual void debugLastCommand() {}
......
......@@ -3356,8 +3356,11 @@ bool isDockVisible(const QString &objectName)
void openMemoryEditor()
{
AddressDialog dialog;
if (dialog.exec() == QDialog::Accepted)
currentEngine()->openMemoryView(dialog.address(), 0, QList<MemoryMarkup>(), QPoint());
if (dialog.exec() == QDialog::Accepted) {
MemoryViewSetupData data;
data.startAddress = dialog.address();
currentEngine()->openMemoryView(data);
}
}
void setThreads(const QStringList &list, int index)
......
......@@ -78,11 +78,11 @@
#include <utils/qtcprocess.h>
#include <utils/savedaction.h>
#include <QBuffer>
#include <QDirIterator>
#include <QTemporaryFile>
#include <QMessageBox>
#include <QPushButton>
#include <QTemporaryFile>
using namespace Core;
using namespace ProjectExplorer;
......@@ -3585,19 +3585,79 @@ void GdbEngine::reloadRegisters()
if (state() != InferiorStopOk && state() != InferiorUnrunnable)
return;
if (!m_registerNamesListed) {
postCommand("-data-list-register-names", CB(handleRegisterListNames));
m_registerNamesListed = true;
if (true) {
if (!m_registerNamesListed) {
postCommand("-data-list-register-names", CB(handleRegisterListNames));
m_registerNamesListed = true;
}
// Can cause i386-linux-nat.c:571: internal-error: Got request
// for bad register number 41.\nA problem internal to GDB has been detected.
postCommand("-data-list-register-values r",
Discardable, CB(handleRegisterListValues));
} else {
postCommand("maintenance print cooked-registers", CB(handleMaintPrintRegisters));
}
}
postCommand("-data-list-register-values r",
Discardable, CB(handleRegisterListValues));
static QByteArray readWord(const QByteArray &ba, int *pos)
{
const int n = ba.size();
while (*pos < n && ba.at(*pos) == ' ')
++*pos;
const int start = *pos;
while (*pos < n && ba.at(*pos) != ' ' && ba.at(*pos) != '\n')
++*pos;
return ba.mid(start, *pos - start);
}
void GdbEngine::setRegisterValue(int nr, const QString &value)
void GdbEngine::handleMaintPrintRegisters(const GdbResponse &response)
{
Register reg = registerHandler()->registers().at(nr);
postCommand("set $" + reg.name + "=" + value.toLatin1());
if (response.resultClass != GdbResultDone)
return;
const QByteArray &ba = response.consoleStreamOutput;
RegisterHandler *handler = registerHandler();
//0 1 2 3 4 5 6
//0123456789012345678901234567890123456789012345678901234567890
// Name Nr Rel Offset Size Type Raw value
// rax 0 0 0 8 int64_t 0x0000000000000000
// rip 16 16 128 8 *1 0x0000000000400dc9
// eflags 17 17 136 4 i386_eflags 0x00000246
// cs 18 18 140 4 int32_t 0x00000033
// xmm15 55 55 516 16 vec128 0x00000000000000000000000000000000
// mxcsr 56 56 532 4 i386_mxcsr 0x00001fa0
// ''
// st6 30 30 224 10 _i387_ext 0x00000000000000000000
// st7 31 31 234 10 _i387_ext 0x00000000000000000000
// fctrl 32 32 244 4 int 0x0000037f
const int n = ba.size();
int pos = 0;
while (true) {
// Skip first line, and until '\n' after each line finished.
while (pos < n && ba.at(pos) != '\n')
++pos;
if (pos >= n)
break;
++pos; // skip \n
Register reg;
reg.name = readWord(ba, &pos);
if (reg.name == "''" || reg.name == "*1:" || reg.name.isEmpty())
continue;
readWord(ba, &pos); // Nr
readWord(ba, &pos); // Rel
readWord(ba, &pos); // Offset
reg.size = readWord(ba, &pos).toInt();
reg.reportedType = readWord(ba, &pos);
reg.value = readWord(ba, &pos);
handler->updateRegister(reg);
}
handler->commitUpdates();
}
void GdbEngine::setRegisterValue(const QByteArray &name, const QString &value)
{
postCommand("set $" + name + "=" + value.toLatin1());
reloadRegisters();
}
......@@ -3608,26 +3668,14 @@ void GdbEngine::handleRegisterListNames(const GdbResponse &response)
return;
}
Registers registers;
int gdbRegisterNumber = 0, internalIndex = 0;
// This both handles explicitly having space for all the registers and
// initializes all indices to 0, giving missing registers a sane default
// in the event of something wacky.
GdbMi names = response.data["register-names"];
m_registerNumbers.resize(names.childCount());
m_registerNames.clear();
int gdbRegisterNumber = 0;
foreach (const GdbMi &item, names.children()) {
// Since we throw away missing registers to eliminate empty rows
// we need to maintain a mapping of GDB register numbers to their
// respective indices in the register list.
if (!item.data().isEmpty()) {
m_registerNumbers[gdbRegisterNumber] = internalIndex++;
registers.append(Register(item.data()));
}
gdbRegisterNumber++;
if (!item.data().isEmpty())
m_registerNames[gdbRegisterNumber] = item.data();
++gdbRegisterNumber;
}
registerHandler()->setRegisters(registers);
}
void GdbEngine::handleRegisterListValues(const GdbResponse &response)
......@@ -3635,19 +3683,46 @@ void GdbEngine::handleRegisterListValues(const GdbResponse &response)
if (response.resultClass != GdbResultDone)
return;
Registers registers = registerHandler()->registers();
const int registerCount = registers.size();
const int gdbRegisterCount = m_registerNumbers.size();
RegisterHandler *handler = registerHandler();
// 24^done,register-values=[{number="0",value="0xf423f"},...]
const GdbMi values = response.data["register-values"];
QTC_ASSERT(registerCount == values.children().size(), return);
foreach (const GdbMi &item, values.children()) {
Register reg;
const int number = item["number"].toInt();
if (number >= 0 && number < gdbRegisterCount)
registers[m_registerNumbers[number]].value = item["value"].data();
reg.name = m_registerNames[number];
QByteArray data = item["value"].data();
if (data.startsWith("0x")) {
reg.value = data;
} else {
// This is what GDB considers machine readable output:
// value="{v4_float = {0x00000000, 0x00000000, 0x00000000, 0x00000000},
// v2_double = {0x0000000000000000, 0x0000000000000000},
// v16_int8 = {0x00 <repeats 16 times>},
// v8_int16 = {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
// v4_int32 = {0x00000000, 0x00000000, 0x00000000, 0x00000000},
// v2_int64 = {0x0000000000000000, 0x0000000000000000},
// uint128 = <error reading variable>}"}
// Try to make sense of it using the int32 chunks:
QByteArray result = "0x";
const int pos1 = data.indexOf("_int32");
const int pos2 = data.indexOf('{', pos1) + 1;
const int pos3 = data.indexOf('}', pos2);
QByteArray inner = data.mid(pos2, pos3 - pos2);
QList<QByteArray> list = inner.split(',');
for (int i = list.size(); --i >= 0; ) {
QByteArray chunk = list.at(i);
if (chunk.startsWith(' '))
chunk.remove(0, 1);
if (chunk.startsWith("0x"))
chunk.remove(0, 2);
QTC_ASSERT(chunk.size() == 8, continue);
result.append(chunk);
}
reg.value = result;
}
handler->updateRegister(reg);
}
registerHandler()->setAndMarkRegisters(registers);
handler->commitUpdates();
}
......
......@@ -347,10 +347,11 @@ private: ////////// View & Data Stuff //////////
// Register specific stuff
//
Q_SLOT void reloadRegisters();
void setRegisterValue(int nr, const QString &value);
void setRegisterValue(const QByteArray &name, const QString &value);
void handleRegisterListNames(const GdbResponse &response);
void handleRegisterListValues(const GdbResponse &response);
QVector<int> m_registerNumbers; // Map GDB register numbers to indices
void handleMaintPrintRegisters(const GdbResponse &response);
QHash<int, QByteArray> m_registerNames; // Map GDB register numbers to indices
//
// Disassembler specific stuff
......
......@@ -1085,16 +1085,15 @@ void LldbEngine::setStackPosition(int index)
void LldbEngine::refreshRegisters(const GdbMi &registers)
{
RegisterHandler *handler = registerHandler();
Registers regs;
foreach (const GdbMi &item, registers.children()) {
Register reg;
reg.name = item["name"].data();
reg.value = item["value"].data();
//reg.type = item["type"].data();
regs.append(reg);
reg.size = item["size"].data().toInt();
reg.reportedType = item["type"].data();
handler->updateRegister(reg);
}
//handler->setRegisters(registers);
handler->setAndMarkRegisters(regs);
handler->commitUpdates();
}
void LldbEngine::refreshThreads(const GdbMi &threads)
......@@ -1250,10 +1249,9 @@ void LldbEngine::changeMemory(MemoryAgent *agent, QObject *editorToken,
runCommand(cmd);
}
void LldbEngine::setRegisterValue(int regnr, const QString &value)
void LldbEngine::setRegisterValue(const QByteArray &name, const QString &value)
{
Register reg = registerHandler()->registers().at(regnr);
runCommand(Command("setRegister").arg("name", reg.name).arg("value", value));
runCommand(Command("setRegister").arg("name", name).arg("value", value));
}
......
......@@ -143,7 +143,7 @@ private:
bool supportsThreads() const { return true; }
bool isSynchronous() const { return true; }
void updateWatchData(const WatchData &data, const WatchUpdateFlags &flags);
void setRegisterValue(int regnr, const QString &value);
void setRegisterValue(const QByteArray &name, const QString &value);
void fetchMemory(Internal::MemoryAgent *, QObject *, quint64 addr, quint64 length);
void changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data);
......
......@@ -135,15 +135,12 @@ void MemoryAgent::connectBinEditorWidget(QWidget *w)
connect(w, SIGNAL(addWatchpointRequested(quint64,uint)), SLOT(handleWatchpointRequest(quint64,uint)));
}
bool MemoryAgent::doCreateBinEditor(quint64 addr, unsigned flags,
const QList<MemoryMarkup> &ml, const QPoint &pos,
QString title, QWidget *parent)
bool MemoryAgent::doCreateBinEditor(const MemoryViewSetupData &data)
{
const bool readOnly = (flags & DebuggerEngine::MemoryReadOnly) != 0;
if (title.isEmpty())
title = tr("Memory at 0x%1").arg(addr, 0, 16);
const bool readOnly = (data.flags & DebuggerEngine::MemoryReadOnly) != 0;
QString title = data.title.isEmpty() ? tr("Memory at 0x%1").arg(data.startAddress, 0, 16) : data.title;
// Separate view?
if (flags & DebuggerEngine::MemoryView) {
if (data.flags & DebuggerEngine::MemoryView) {
// Ask BIN editor plugin for factory service and have it create a bin editor widget.
QWidget *binEditor = 0;
if (QObject *factory = ExtensionSystem::PluginManager::getObjectByClassName(QLatin1String("BinEditor::BinEditorWidgetFactory")))
......@@ -155,24 +152,22 @@ bool MemoryAgent::doCreateBinEditor(quint64 addr, unsigned flags,
MemoryView::setBinEditorNewWindowRequestAllowed(binEditor, true);
MemoryView *topLevel = 0;
// Memory view tracking register value, providing its own updating mechanism.
if (flags & DebuggerEngine::MemoryTrackRegister) {
RegisterMemoryView *rmv = new RegisterMemoryView(binEditor, parent);
rmv->init(m_engine->registerHandler(), int(addr));
topLevel = rmv;
if (data.flags & DebuggerEngine::MemoryTrackRegister) {
topLevel = new RegisterMemoryView(binEditor, data.startAddress, data.registerName, m_engine->registerHandler(), data.parent);
} else {
// Ordinary memory view
MemoryView::setBinEditorMarkup(binEditor, ml);
MemoryView::setBinEditorRange(binEditor, addr, MemoryAgent::DataRange, MemoryAgent::BinBlockSize);
topLevel = new MemoryView(binEditor, parent);
MemoryView::setBinEditorMarkup(binEditor, data.markup);
MemoryView::setBinEditorRange(binEditor, data.startAddress, MemoryAgent::DataRange, MemoryAgent::BinBlockSize);
topLevel = new MemoryView(binEditor, data.parent);
topLevel->setWindowTitle(title);
}
m_views << topLevel;
topLevel->move(pos);
topLevel->move(data.pos);
topLevel->show();
return true;
}
// Editor: Register tracking not supported.
QTC_ASSERT(!(flags & DebuggerEngine::MemoryTrackRegister), return false);
QTC_ASSERT(!(data.flags & DebuggerEngine::MemoryTrackRegister), return false);
if (!title.endsWith(QLatin1Char('$')))
title.append(QLatin1String(" $"));
IEditor *editor = EditorManager::openEditorWithContents(
......@@ -185,17 +180,15 @@ bool MemoryAgent::doCreateBinEditor(quint64 addr, unsigned flags,
connectBinEditorWidget(editorBinEditor);
MemoryView::setBinEditorReadOnly(editorBinEditor, readOnly);
MemoryView::setBinEditorNewWindowRequestAllowed(editorBinEditor, true);
MemoryView::setBinEditorRange(editorBinEditor, addr, MemoryAgent::DataRange, MemoryAgent::BinBlockSize);
MemoryView::setBinEditorMarkup(editorBinEditor, ml);
MemoryView::setBinEditorRange(editorBinEditor, data.startAddress, MemoryAgent::DataRange, MemoryAgent::BinBlockSize);
MemoryView::setBinEditorMarkup(editorBinEditor, data.markup);
m_editors << editor;
return true;
}
void MemoryAgent::createBinEditor(quint64 addr, unsigned flags,
const QList<MemoryMarkup> &ml, const QPoint &pos,
const QString &title, QWidget *parent)
void MemoryAgent::createBinEditor(const MemoryViewSetupData &data)
{
if (!doCreateBinEditor(addr, flags, ml, pos, title, parent))
if (!doCreateBinEditor(data))
Core::AsynchronousMessageBox::warning(
tr("No Memory Viewer Available"),
tr("The memory contents cannot be shown as no viewer plugin "
......@@ -204,7 +197,9 @@ void MemoryAgent::createBinEditor(quint64 addr, unsigned flags,
void MemoryAgent::createBinEditor(quint64 addr)
{
createBinEditor(addr, 0, QList<MemoryMarkup>(), QPoint(), QString(), 0);
MemoryViewSetupData data;
data.startAddress = addr;
createBinEditor(data);
}
void MemoryAgent::fetchLazyData(quint64 block)
......
......@@ -34,11 +34,10 @@
#include "debuggerconstants.h"
#include <QObject>
#include <QPoint>
#include <QPointer>
#include <QColor>
QT_FORWARD_DECLARE_CLASS(QPoint)
namespace Core { class IEditor; }
namespace ProjectExplorer { class Abi; }
......@@ -62,6 +61,20 @@ public:
QString toolTip;
};
class MemoryViewSetupData
{
public:
MemoryViewSetupData() : parent(0), startAddress(0), flags(0) {}
QWidget *parent;
quint64 startAddress;
QByteArray registerName;
unsigned flags;
QList<Internal::MemoryMarkup> markup;
QPoint pos;
QString title;
};
class MemoryAgent : public QObject
{
Q_OBJECT
......@@ -80,9 +93,7 @@ public:
public slots:
// Called by engine to create a new view.
void createBinEditor(quint64 startAddr, unsigned flags,
const QList<MemoryMarkup> &ml, const QPoint &pos,
const QString &title, QWidget *parent);
void createBinEditor(const MemoryViewSetupData &data);