diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index 778bdb7471a..6500a7e2466 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -127,6 +127,7 @@ false { include(../../shared/modeltest/modeltest.pri) #DEFINES += USE_WATCH_MODEL_TEST=1 #DEFINES += USE_BREAK_MODEL_TEST=1 + #DEFINES += USE_REGISTER_MODEL_TEST=1 } win32 { include(../../shared/registryaccess/registryaccess.pri) diff --git a/src/plugins/debugger/registerhandler.cpp b/src/plugins/debugger/registerhandler.cpp index bc9d8248a57..ebd739b95a9 100644 --- a/src/plugins/debugger/registerhandler.cpp +++ b/src/plugins/debugger/registerhandler.cpp @@ -33,29 +33,231 @@ #include "registerhandler.h" #include "watchdelegatewidgets.h" +#if USE_REGISTER_MODEL_TEST +#include "modeltest.h" +#endif + +#include + +namespace Debugger { +namespace Internal { + +////////////////////////////////////////////////////////////////// +// +// Register +// +////////////////////////////////////////////////////////////////// + +enum RegisterType +{ + RegisterUnknown, + //RegisterDummy, // like AH if EAX is present. + RegisterI8, + RegisterI16, + RegisterI32, + RegisterI64, + RegisterF32, + RegisterF64, + RegisterF80, + RegisterXMM, + RegisterMMX, + RegisterNeon, + RegisterFlags32 +}; + +static struct RegisterNameAndType +{ + const char *name; + RegisterType type; +} theNameAndType[] = { + { "eax", RegisterI32 }, + { "ecx", RegisterI32 }, + { "edx", RegisterI32 }, + { "ebx", RegisterI32 }, + { "esp", RegisterI32 }, + { "ebp", RegisterI32 }, + { "esi", RegisterI32 }, + { "edi", RegisterI32 }, + { "eip", RegisterI32 }, + { "eflags", RegisterFlags32 }, + { "cs", RegisterI32 }, + { "ss", RegisterI32 }, + { "ds", RegisterI32 }, + { "es", RegisterI32 }, + { "fs", RegisterI32 }, + { "gs", RegisterI32 }, + { "st0", RegisterF80 }, + { "st1", RegisterF80 }, + { "st2", RegisterF80 }, + { "st3", RegisterF80 }, + { "st4", RegisterF80 }, + { "st5", RegisterF80 }, + { "st6", RegisterF80 }, + { "st7", RegisterF80 }, + { "fctrl", RegisterFlags32 }, + { "fstat", RegisterFlags32 }, + { "ftag", RegisterFlags32 }, + { "fiseg", RegisterFlags32 }, + { "fioff", RegisterFlags32 }, + { "foseg", RegisterFlags32 }, + { "fooff", RegisterFlags32 }, + { "fop", RegisterFlags32 }, + { "xmm0", RegisterXMM }, + { "xmm1", RegisterXMM }, + { "xmm2", RegisterXMM }, + { "xmm3", RegisterXMM }, + { "xmm4", RegisterXMM }, + { "xmm5", RegisterXMM }, + { "xmm6", RegisterXMM }, + { "xmm7", RegisterXMM }, + { "mxcsr", RegisterFlags32 }, + { "orig_eax", RegisterI32 }, + { "al", RegisterI8 }, + { "cl", RegisterI8 }, + { "dl", RegisterI8 }, + { "bl", RegisterI8 }, + { "ah", RegisterI8 }, + { "ch", RegisterI8 }, + { "dh", RegisterI8 }, + { "bh", RegisterI8 }, + { "ax", RegisterI16 }, + { "cx", RegisterI16 }, + { "dx", RegisterI16 }, + { "bx", RegisterI16 }, + { "bp", RegisterI16 }, + { "si", RegisterI16 }, + { "di", RegisterI16 }, + { "mm0", RegisterMMX }, + { "mm1", RegisterMMX }, + { "mm2", RegisterMMX }, + { "mm3", RegisterMMX }, + { "mm4", RegisterMMX }, + { "mm5", RegisterMMX }, + { "mm6", RegisterMMX }, + { "mm7", RegisterMMX } + }; + +static RegisterType guessType(const QByteArray &name) +{ + static QHash theTypes; + if (theTypes.isEmpty()) { + for (int i = 0; i != sizeof(theNameAndType) / sizeof(theNameAndType[0]); ++i) + theTypes[theNameAndType[i].name] = theNameAndType[i].type; + } + return theTypes.value(name, RegisterUnknown); +} + +static int childCountFromType(int type) +{ + switch (type) { + case RegisterUnknown: return 0; + case RegisterI8: return 0; + case RegisterI16: return 1; + case RegisterI32: return 2; + case RegisterI64: return 3; + case RegisterF32: return 0; + case RegisterF64: return 0; + case RegisterF80: return 0; + case RegisterXMM: return 3; + case RegisterMMX: return 3; + case RegisterNeon: return 3; + case RegisterFlags32: return 0; + } + QTC_ASSERT(false, /**/); + return 0; +} + +static int bitWidthFromType(int type, int subType) +{ + const uint integer[] = { 8, 16, 32, 64 }; + const uint xmm[] = { 8, 16, 32, 64 }; + const uint mmx[] = { 8, 16, 32, 64 }; + const uint neon[] = { 8, 16, 32, 64 }; + + switch (type) { + case RegisterUnknown: return 0; + case RegisterI8: return 8; + case RegisterI16: return integer[subType]; + case RegisterI32: return integer[subType]; + case RegisterI64: return integer[subType]; + case RegisterF32: return 0; + case RegisterF64: return 0; + case RegisterF80: return 0; + case RegisterXMM: return xmm[subType]; + case RegisterMMX: return mmx[subType]; + case RegisterNeon: return neon[subType]; + case RegisterFlags32: return 0; + } + QTC_ASSERT(false, /**/); + return 0; +} + +Register::Register(const QByteArray &name_) + : name(name_), changed(true) +{ + type = guessType(name); +} + + ////////////////////////////////////////////////////////////////// // // RegisterHandler // ////////////////////////////////////////////////////////////////// -namespace Debugger { -namespace Internal { - RegisterHandler::RegisterHandler() - : m_base(-1) { - setNumberBase(16); + m_base = 16; + calculateWidth(); +#if USE_REGISTER_MODEL_TEST + new ModelTest(this, 0); +#endif } -int RegisterHandler::rowCount(const QModelIndex &parent) const +int RegisterHandler::rowCount(const QModelIndex &idx) const { - return parent.isValid() ? 0 : m_registers.size(); + if (idx.column() > 0) + return 0; + if (!idx.isValid()) + return m_registers.size(); // Top level. + if (idx.internalId() >= 0) + return 0; // Sub-Items don't have children. + if (idx.row() >= m_registers.size()) + return 0; + return childCountFromType(m_registers[idx.row()].type); } -int RegisterHandler::columnCount(const QModelIndex &parent) const +int RegisterHandler::columnCount(const QModelIndex &idx) const { - return parent.isValid() ? 0 : 2; + if (idx.column() > 0) + return 0; + if (!idx.isValid()) + return 2; + if (idx.internalId() >= 0) + return 0; // Sub-Items don't have children. + return 2; +} + +QModelIndex RegisterHandler::index(int row, int col, const QModelIndex &parent) const +{ + if (row < 0 || col < 0 || col >= 2) + return QModelIndex(); + if (!parent.isValid()) // Top level. + return createIndex(row, col, -1); + if (parent.internalId() >= 0) // Sub-Item has no children. + return QModelIndex(); + if (parent.column() > 0) + return QModelIndex(); + return createIndex(row, col, parent.row()); +} + +QModelIndex RegisterHandler::parent(const QModelIndex &idx) const +{ + if (!idx.isValid()) + return QModelIndex(); + if (idx.internalId() >= 0) + return createIndex(idx.internalId(), 0, -1); + return QModelIndex(); } // Editor value: Preferably number, else string. @@ -83,27 +285,67 @@ QString Register::displayValue(int base, int strlen) const QVariant RegisterHandler::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() >= m_registers.size()) + if (!index.isValid()) return QVariant(); - const Register ® = m_registers.at(index.row()); + QModelIndex topLevel = index.parent(); + const int mainRow = topLevel.isValid() ? topLevel.row() : index.row(); - switch (role) { - case Qt::DisplayRole: - switch (index.column()) { - case 0: { - const QString padding = QLatin1String(" "); - return QVariant(padding + reg.name + padding); + if (mainRow >= m_registers.size()) + return QVariant(); + + + const Register ® = m_registers.at(mainRow); + + if (topLevel.isValid()) { + // + // Nested + // + int subType = index.row(); + int bitWidth = bitWidthFromType(reg.type, subType); + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case 0: { + switch (bitWidth) { + case 8: return "[Bytes]"; + case 16: return "[Words]"; + case 32: return "[DWords]"; + case 64: return "[QWords]"; + case -32: return "[Single]"; + case -64: return "[Double]"; + return QVariant(bitWidth); + } + } + } + default: + break; + } + + } else { + // + // Toplevel + // + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case 0: { + const QString padding = QLatin1String(" "); + return QVariant(padding + reg.name + padding); + //return QVariant(reg.name); + } + case 1: // Display: Pad value for alignment + return reg.displayValue(m_base, m_strlen); + } // switch column + case Qt::EditRole: // Edit: Unpadded for editing + return reg.editValue(); + case Qt::TextAlignmentRole: + return index.column() == 1 ? QVariant(Qt::AlignRight) : QVariant(); + default: + break; } - case 1: // Display: Pad value for alignment - return reg.displayValue(m_base, m_strlen); - } // switch column - case Qt::EditRole: // Edit: Unpadded for editing - return reg.editValue(); - case Qt::TextAlignmentRole: - return index.column() == 1 ? QVariant(Qt::AlignRight) : QVariant(); - default: - break; } return QVariant(); } @@ -113,8 +355,8 @@ QVariant RegisterHandler::headerData(int section, Qt::Orientation orientation, { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { - case 0: return tr("Name"); - case 1: return tr("Value (base %1)").arg(m_base); + case 0: return tr("Name"); + case 1: return tr("Value (Base %1)").arg(m_base); }; } return QVariant(); @@ -128,7 +370,7 @@ Qt::ItemFlags RegisterHandler::flags(const QModelIndex &idx) const const Qt::ItemFlags notEditable = Qt::ItemIsSelectable|Qt::ItemIsEnabled; // Can edit registers if they are hex numbers and not arrays. if (idx.column() == 1 - && IntegerWatchLineEdit::isUnsignedHexNumber(m_registers.at(idx.row()).value)) + && IntegerWatchLineEdit::isUnsignedHexNumber(m_registers.at(idx.row()).value)) return notEditable | Qt::ItemIsEditable; return notEditable; } @@ -173,15 +415,16 @@ void RegisterHandler::setAndMarkRegisters(const Registers ®isters) return; } const int size = m_registers.size(); - for (int r = 0; r < size; r++) { - const QModelIndex regIndex = index(r, 1); + for (int r = 0; r != size; ++r) { + const QModelIndex regIndex = index(r, 1, QModelIndex()); if (m_registers.at(r).value != registers.at(r).value) { // Indicate red if values change, keep changed. - m_registers[r].changed = m_registers[r].changed || !m_registers.at(r).value.isEmpty(); + m_registers[r].changed = m_registers.at(r).changed + || !m_registers.at(r).value.isEmpty(); m_registers[r].value = registers.at(r).value; emit dataChanged(regIndex, regIndex); } - emit registerSet(regIndex); // notify attached memory views. + emit registerSet(regIndex); // Notify attached memory views. } } diff --git a/src/plugins/debugger/registerhandler.h b/src/plugins/debugger/registerhandler.h index 06d265c5701..5c2ac6adb32 100644 --- a/src/plugins/debugger/registerhandler.h +++ b/src/plugins/debugger/registerhandler.h @@ -42,8 +42,8 @@ namespace Internal { class Register { public: - Register() : changed(true) {} - Register(const QByteArray &name_) : name(name_), changed(true) {} + Register() : type(0), changed(true) {} + Register(const QByteArray &name_); QVariant editValue() const; QString displayValue(int base, int strlen) const; @@ -55,6 +55,7 @@ public: * Values that cannot be converted (such as 128bit MMX-registers) are * passed through. */ QString value; + int type; bool changed; }; @@ -85,8 +86,10 @@ signals: private: void calculateWidth(); - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &idx = QModelIndex()) const; + int columnCount(const QModelIndex &idx = QModelIndex()) const; + QModelIndex index(int row, int col, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &idx) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; diff --git a/src/plugins/debugger/registerwindow.cpp b/src/plugins/debugger/registerwindow.cpp index 491f314bb3f..735894b3f74 100644 --- a/src/plugins/debugger/registerwindow.cpp +++ b/src/plugins/debugger/registerwindow.cpp @@ -93,6 +93,7 @@ public: lineEdit->setBase(big ? 16 : base); lineEdit->setSigned(false); lineEdit->setAlignment(Qt::AlignRight); + lineEdit->setFrame(false); return lineEdit; } @@ -199,7 +200,8 @@ void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev) return; const Register &aRegister = handler->registers().at(idx.row()); const QVariant addressV = aRegister.editValue(); - const quint64 address = addressV.type() == QVariant::ULongLong ? addressV.toULongLong() : 0; + const quint64 address = addressV.type() == QVariant::ULongLong + ? addressV.toULongLong() : 0; QAction *actViewMemory = menu.addAction(QString()); QAction *actEditMemory = menu.addAction(QString()); @@ -212,8 +214,9 @@ void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev) actEditMemory->setText(tr("Open Memory Editor at 0x%1").arg(address, 0, 16)); actEditMemory->setEnabled(canShow); actViewMemory->setText(tr("Open Memory View at Value of Register %1 0x%2") - .arg(QString::fromAscii(aRegister.name)).arg(address, 0, 16)); - actShowDisassemblerAt->setText(tr("Open Disassembler at 0x%1").arg(address, 0, 16)); + .arg(QString::fromAscii(aRegister.name)).arg(address, 0, 16)); + actShowDisassemblerAt->setText(tr("Open Disassembler at 0x%1") + .arg(address, 0, 16)); actShowDisassemblerAt->setEnabled(engineCapabilities & DisassemblerCapability); } else { actEditMemory->setText(tr("Open Memory Editor")); @@ -255,11 +258,13 @@ void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev) engine->reloadRegisters(); else if (act == actEditMemory) { const QString registerName = QString::fromAscii(aRegister.name, address); - engine->openMemoryView(address, 0, RegisterMemoryView::registerMarkup(address, registerName), - QPoint(), RegisterMemoryView::title(registerName), 0); + engine->openMemoryView(address, 0, + RegisterMemoryView::registerMarkup(address, registerName), + QPoint(), RegisterMemoryView::title(registerName), 0); } else if (act == actViewMemory) { - engine->openMemoryView(idx.row(), DebuggerEngine::MemoryTrackRegister|DebuggerEngine::MemoryView, - QList(), position, QString(), this); + engine->openMemoryView(idx.row(), + DebuggerEngine::MemoryTrackRegister|DebuggerEngine::MemoryView, + QList(), position, QString(), this); } else if (act == actShowDisassembler) { AddressDialog dialog; if (address) @@ -295,7 +300,6 @@ void RegisterWindow::setAlwaysResizeColumnsToContents(bool on) void RegisterWindow::setModel(QAbstractItemModel *model) { QTreeView::setModel(model); - setAlwaysResizeColumnsToContents(true); if (header()) { bool adjust = debuggerCore()->boolSetting(AlwaysAdjustRegistersColumnWidths); setAlwaysResizeColumnsToContents(adjust);