diff --git a/kdbg/dbgdriver.h b/kdbg/dbgdriver.h index ae693146..6358372e 100644 --- a/kdbg/dbgdriver.h +++ b/kdbg/dbgdriver.h @@ -266,6 +266,7 @@ struct MemoryDump DbgAddr address; QString dump; bool littleendian = true; + bool endOfDump = false; }; /** @@ -592,6 +593,7 @@ class DebuggerDriver : public QProcess virtual QString makeCmdString(DbgCommand cmd, QString strArg, int intArg) = 0; virtual QString makeCmdString(DbgCommand cmd, QString strArg1, QString strArg2) = 0; virtual QString makeCmdString(DbgCommand cmd, int intArg1, int intArg2) = 0; + virtual QString makeCmdString(DbgCommand cmd, QString strArg, int intArg1, int intArg2) = 0; protected: void processOutput(const QByteArray& data); diff --git a/kdbg/debugger.cpp b/kdbg/debugger.cpp index 5a022a3f..ddf1642b 100644 --- a/kdbg/debugger.cpp +++ b/kdbg/debugger.cpp @@ -63,6 +63,7 @@ KDebugger::KDebugger(QWidget* parent, QObject(parent), m_ttyLevel(ttyFull), m_memoryFormat(MDTword | MDThex), + m_memoryLength(16), m_haveExecutable(false), m_programActive(false), m_programRunning(false), @@ -1282,8 +1283,8 @@ void KDebugger::updateAllExprs() m_d->queueCmd(DCinforegisters); // get new memory dump - if (!m_memoryExpression.isEmpty()) { - queueMemoryDump(false); + if (!m_memoryStartExpression.isEmpty()) { + queueMemoryDump(false, true); } // update watch expressions @@ -2157,25 +2158,36 @@ void KDebugger::setThread(int id) m_d->queueCmdPrio(DCthread, id); } -void KDebugger::setMemoryExpression(const QString& memexpr) +void KDebugger::setMemoryExpression(const QString& start_memexpr, unsigned total_length, + const QString& current_memexpr, unsigned current_length) { - m_memoryExpression = memexpr; + m_memoryExpression = current_memexpr; + m_memoryLength = current_length; + m_memoryStartExpression = start_memexpr; + m_memoryTotalLength = total_length; // queue the new expression if (!m_memoryExpression.isEmpty() && isProgramActive() && !isProgramRunning()) { - queueMemoryDump(true); + queueMemoryDump(true, false); } } -void KDebugger::queueMemoryDump(bool immediate) +void KDebugger::queueMemoryDump(bool immediate, bool update) { - if (immediate) - m_d->queueCmdPrio(DCexamine, m_memoryExpression, m_memoryFormat); - else - m_d->queueCmd(DCexamine, m_memoryExpression, m_memoryFormat); + if (update) { + if (immediate) + m_d->queueCmdPrio(DCexamine, m_memoryStartExpression, m_memoryFormat, m_memoryTotalLength); + else + m_d->queueCmd(DCexamine, m_memoryStartExpression, m_memoryFormat, m_memoryTotalLength); + } else { + if (immediate) + m_d->queueCmdPrio(DCexamine, m_memoryExpression, m_memoryFormat, m_memoryLength); + else + m_d->queueCmd(DCexamine, m_memoryExpression, m_memoryFormat, m_memoryLength); + } } void KDebugger::handleMemoryDump(const char* output) diff --git a/kdbg/debugger.h b/kdbg/debugger.h index 536573b9..ee9ce0d6 100644 --- a/kdbg/debugger.h +++ b/kdbg/debugger.h @@ -337,8 +337,11 @@ public slots: /** * The memory at that the expression evaluates to is watched. Can be * empty. Triggers a redisplay even if the expression did not change. + * Memory expression start and total length is used to update all bytes of memory when + * is necessary, to request new parts of memory is used current_memexpr and current_length */ - void setMemoryExpression(const QString& memexpr); + void setMemoryExpression(const QString& start_memexpr, unsigned total_length, + const QString& current_memexpr, unsigned current_length); /** * Sets how the watched memory location is displayed. @@ -362,6 +365,9 @@ public slots: std::list m_brkpts; QString m_memoryExpression; /* memory location to watch */ unsigned m_memoryFormat; /* how that output should look */ + unsigned m_memoryLength; /* memory length to watch */ + QString m_memoryStartExpression; /* start memory location to watch */ + unsigned m_memoryTotalLength; /* memory total length to watch */ protected slots: void parse(CmdQueueItem* cmd, const char* output); @@ -393,7 +399,7 @@ protected slots: void evalStructExpression(VarTree* var, ExprWnd* wnd, bool immediate); void dereferencePointer(ExprWnd* wnd, VarTree* var, bool immediate); void determineType(ExprWnd* wnd, VarTree* var); - void queueMemoryDump(bool immediate); + void queueMemoryDump(bool immediate, bool update); CmdQueueItem* loadCoreFile(); void openProgramConfig(const QString& name); diff --git a/kdbg/gdbdriver.cpp b/kdbg/gdbdriver.cpp index 484d83a1..668af739 100644 --- a/kdbg/gdbdriver.cpp +++ b/kdbg/gdbdriver.cpp @@ -464,12 +464,44 @@ QString GdbDriver::makeCmdString(DbgCommand cmd, int intArg) return cmdString; } +QString GdbDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg1, int intArg2) +{ + assert(cmd == DCexamine); + + normalizeStringArg(strArg); + + QString cmdString; + + if (cmd == DCexamine) { + // make a format specifier from the intArg + static const char size[16] = { + '\0', 'b', 'h', 'w', 'g' + }; + static const char format[16] = { + '\0', 'x', 'd', 'u', 'o', 't', + 'a', 'c', 'f', 's', 'i' + }; + assert(MDTsizemask == 0xf); /* lowest 4 bits */ + assert(MDTformatmask == 0xf0); /* next 4 bits */ + int count = intArg2; /* number of entities to print */ + char sizeSpec = size[intArg1 & MDTsizemask]; + char formatSpec = format[(intArg1 & MDTformatmask) >> 4]; + assert(sizeSpec != '\0'); + assert(formatSpec != '\0'); + + QString spec; + spec.sprintf("/%d%c%c", count, sizeSpec, formatSpec); + cmdString = makeCmdString(DCexamine, spec, strArg); + } + + return cmdString; +} + QString GdbDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg) { assert(cmd >= 0 && cmd < NUM_CMDS); assert(cmds[cmd].argsNeeded == GdbCmdInfo::argStringNum || cmds[cmd].argsNeeded == GdbCmdInfo::argNumString || - cmd == DCexamine || cmd == DCtty); normalizeStringArg(strArg); @@ -504,45 +536,6 @@ QString GdbDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg) return makeCmdString(DCtty, strArg); /* note: no problem if strArg empty */ } - if (cmd == DCexamine) { - // make a format specifier from the intArg - static const char size[16] = { - '\0', 'b', 'h', 'w', 'g' - }; - static const char format[16] = { - '\0', 'x', 'd', 'u', 'o', 't', - 'a', 'c', 'f', 's', 'i' - }; - assert(MDTsizemask == 0xf); /* lowest 4 bits */ - assert(MDTformatmask == 0xf0); /* next 4 bits */ - int count = 16; /* number of entities to print */ - char sizeSpec = size[intArg & MDTsizemask]; - char formatSpec = format[(intArg & MDTformatmask) >> 4]; - assert(sizeSpec != '\0'); - assert(formatSpec != '\0'); - // adjust count such that 16 lines are printed - switch (intArg & MDTformatmask) { - case MDTstring: case MDTinsn: - break; /* no modification needed */ - default: - // all cases drop through: - switch (intArg & MDTsizemask) { - case MDTbyte: - case MDThalfword: - count *= 2; - case MDTword: - count *= 2; - case MDTgiantword: - count *= 2; - } - break; - } - QString spec; - spec.sprintf("/%d%c%c", count, sizeSpec, formatSpec); - - return makeCmdString(DCexamine, spec, strArg); - } - if (cmds[cmd].argsNeeded == GdbCmdInfo::argStringNum) { // line numbers are zero-based @@ -2683,10 +2676,14 @@ QString GdbDriver::parseMemoryDump(const char* output, std::list& me MemoryDump md; // the address + skipSpace(p); /* remove leading space, detected on instructions output format */ + const char* start = p; + while (*p != '\0' && *p != ':' && !isspace(*p)) p++; md.address = QString::fromLatin1(start, p-start); + if (*p != ':') { // parse function offset skipSpace(p); @@ -2711,6 +2708,9 @@ QString GdbDriver::parseMemoryDump(const char* output, std::list& me md.littleendian = m_littleendian; memdump.push_back(md); } + + if (end_region && !memdump.empty()) + memdump.back().endOfDump = true; return QString(); } diff --git a/kdbg/gdbdriver.h b/kdbg/gdbdriver.h index cb27e455..af73f384 100644 --- a/kdbg/gdbdriver.h +++ b/kdbg/gdbdriver.h @@ -65,6 +65,7 @@ class GdbDriver : public DebuggerDriver virtual QString makeCmdString(DbgCommand cmd, QString strArg, int intArg); virtual QString makeCmdString(DbgCommand cmd, QString strArg1, QString strArg2); virtual QString makeCmdString(DbgCommand cmd, int intArg1, int intArg2); + virtual QString makeCmdString(DbgCommand cmd, QString strArg, int intArg1, int intArg2); virtual int findPrompt(const QByteArray& output) const; void parseMarker(CmdQueueItem* cmd); }; diff --git a/kdbg/memwindow.cpp b/kdbg/memwindow.cpp index 3a015414..1cb3a491 100644 --- a/kdbg/memwindow.cpp +++ b/kdbg/memwindow.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ const int COL_ADDR = 0; const int COL_DUMP_ASCII = 9; +const int MAX_COL = 10; MemoryWindow::MemoryWindow(QWidget* parent) : QWidget(parent), @@ -34,24 +36,24 @@ MemoryWindow::MemoryWindow(QWidget* parent) : m_expression.setMaxCount(15); m_memory.setColumnCount(10); - m_memory.setHeaderLabel(i18n("Address")); - m_memory.header()->setSectionResizeMode(COL_ADDR, QHeaderView::Fixed); - for (int i = 1; i < 10; i++) { - m_memory.hideColumn(i); - m_memory.header()->setSectionResizeMode(i, QHeaderView::ResizeToContents); - m_memory.headerItem()->setText(i, QString()); + for (int i = 0; i < MAX_COL; i++) { + m_memory.header()->setSectionResizeMode(i, QHeaderView::Fixed); } - m_memory.header()->setSectionResizeMode(COL_DUMP_ASCII, QHeaderView::Fixed); m_memory.header()->setStretchLastSection(false); m_memory.setSortingEnabled(false); /* don't sort */ m_memory.setAllColumnsShowFocus(true); m_memory.setRootIsDecorated(false); - m_memory.header()->setSectionResizeMode(QHeaderView::ResizeToContents); m_memory.header()->setSectionsClickable(false); m_memory.header()->setSectionsMovable(false); // don't move columns + m_memory.setHeaderHidden(true); // hide header m_memory.setContextMenuPolicy(Qt::NoContextMenu); // defer to parent + // get row height + new QTreeWidgetItem(&m_memory, QStringList("0x179bf")); + m_memoryRowHeight = m_memory.visualRect(m_memory.indexAt(QPoint(0, 0))).height(); + m_memory.clear(); + // create layout m_layout.setSpacing(2); m_layout.addWidget(&m_expression, 0); @@ -62,6 +64,10 @@ MemoryWindow::MemoryWindow(QWidget* parent) : this, SLOT(slotNewExpression(const QString&))); connect(m_expression.lineEdit(), SIGNAL(returnPressed()), this, SLOT(slotNewExpression())); + connect(m_memory.verticalScrollBar(), SIGNAL(valueChanged(int)), + this, SLOT(verticalScrollBarMoved(int))); + connect(m_memory.verticalScrollBar(), SIGNAL(rangeChanged(int, int)), + this, SLOT(verticalScrollBarRangeChanged(int, int))); // the popup menu QAction* pAction; @@ -101,6 +107,61 @@ MemoryWindow::~MemoryWindow() { } +void MemoryWindow::requestMemoryDump(const QString &expr) +{ + /* + * Memory dump same length for all cases + * Length of memory dump to request is n_rows (window size / row_height) * column_size + * Column size + * Byte 8 + * Halfword 8 + * Word 4 + * Giantword 2 + * Strings and instructions format are column size 1 + */ + int nrows = m_memory.height() / m_memoryRowHeight; + unsigned int colum_size = 1; + unsigned int format = m_format & MDTformatmask; + + if (m_dumpMemRegionEnd) { + return; + } + + /* Move to driver ???? */ + if (!(format == MDTinsn || format == MDTstring)) { + switch(m_format & MDTsizemask) { + case MDTbyte: + case MDThalfword: + colum_size= 8; + break; + case MDTword: + colum_size= 4; + break; + case MDTgiantword: + colum_size= 2; + break; + } + } + unsigned request_length = nrows * colum_size * 2; + m_dumpLength += request_length; + m_debugger->setMemoryExpression(m_expression.lineEdit()->text(), m_dumpLength, expr, request_length); +} + +void MemoryWindow::verticalScrollBarRangeChanged(int min, int max) +{ + if (min == 0 && max == 0 && !m_dumpLastAddr.isEmpty()) { + requestMemoryDump(m_dumpLastAddr.asString()); + } +} + +void MemoryWindow::verticalScrollBarMoved(int value) +{ + int scrollmax = m_memory.verticalScrollBar()->maximum(); + if (value == scrollmax && !m_dumpLastAddr.isEmpty()) { + requestMemoryDump(m_dumpLastAddr.asString()); + } +} + void MemoryWindow::contextMenuEvent(QContextMenuEvent* ev) { m_popup.popup(ev->globalPos()); @@ -143,22 +204,28 @@ void MemoryWindow::slotNewExpression(const QString& newText) void MemoryWindow::displayNewExpression(const QString& expr) { - m_debugger->setMemoryExpression(expr); - m_expression.setEditText(expr); + // Clear variables + m_dumpMemRegionEnd = false; + m_dumpLastAddr = DbgAddr{}; + m_dumpLength = 0; - // clear memory dump if no dump wanted - if (expr.isEmpty()) { - m_memory.clear(); - m_old_memory.clear(); + if (m_memory.verticalScrollBar()) + m_memory.verticalScrollBar()->setValue(0); + + m_memory.clear(); + m_memoryColumnsWidth.clear(); + for (int i = 0; i < MAX_COL; i++) { + m_memoryColumnsWidth.append(0); } + + requestMemoryDump(expr); + m_expression.setEditText(expr); } void MemoryWindow::slotTypeChange(QAction* action) { int id = action->data().toInt(); - m_old_memory.clear(); - // compute new type if (id & MDTsizemask) m_format = (m_format & ~MDTsizemask) | id; @@ -212,11 +279,17 @@ QString MemoryWindow::parseMemoryDumpLineToAscii(const QString& line, bool littl void MemoryWindow::slotNewMemoryDump(const QString& msg, const std::list& memdump) { - m_memory.clear(); + QFontMetrics fm(font()); + if (!msg.isEmpty()) { - new QTreeWidgetItem(&m_memory, QStringList() << QString() << msg); + for (int i = MAX_COL; i > 0; i--) { + m_memory.setColumnHidden(i, true); + } + new QTreeWidgetItem(&m_memory, QStringList() << msg); + m_memory.header()->resizeSection(COL_ADDR, fm.width(msg)+10); return; } + bool showDumpAscii = (m_format & MDTformatmask) == MDThex; std::list::const_iterator md = memdump.begin(); @@ -228,58 +301,67 @@ void MemoryWindow::slotNewMemoryDump(const QString& msg, const std::list tmpMap; - - QFontMetrics fm(font()); - int maxWidthAddr = 0; - int maxWidthAscii = 0; - for (; md != memdump.end(); ++md) { QString addr = md->address.asString() + " " + md->address.fnoffs; QStringList sl = md->dump.split( "\t" ); - if (fm.width(addr) > maxWidthAddr) { - maxWidthAddr = fm.width(addr); + if (fm.width(addr) > m_memoryColumnsWidth[COL_ADDR]) { + m_memoryColumnsWidth[COL_ADDR] = fm.width(addr); } - // save memory - tmpMap[addr] = md->dump; + QTreeWidgetItem* line = nullptr; + QList items = m_memory.findItems(addr, Qt::MatchExactly, 0); + if (items.count() != 0) { + line = items.at(0); + } + if (!line) { // line not found in memory view, append new one + for (int i = 0; i < sl.count(); i++) { + if (fm.width(sl[i]) > m_memoryColumnsWidth[i+1]) { + m_memoryColumnsWidth[i+1] = fm.width(sl[i]); + } + } - QTreeWidgetItem* line = - new QTreeWidgetItem(&m_memory, QStringList(addr) << sl); + line = new QTreeWidgetItem(&m_memory, QStringList(addr) << sl); + m_dumpLastAddr = md->address; + if (md->endOfDump) + m_dumpMemRegionEnd = true; + } else { // line found in memory view updated it + for (int i = 0; i < sl.count(); i++) { + if (fm.width(sl[i]) > m_memoryColumnsWidth[i+1]) { + m_memoryColumnsWidth[i+1] = fm.width(sl[i]); + } + bool changed = i < (line->columnCount() - showDumpAscii) && sl[i] != line->text(i+1); + line->setForeground(i+1, changed ? QBrush(QColor(Qt::red)) : palette().text()); + line->setText(i+1, sl[i]); + } + } if (showDumpAscii) { QString dumpAscii = parseMemoryDumpLineToAscii(md->dump, md->littleendian); - line->setText(COL_DUMP_ASCII, dumpAscii); + /* + * Add space padding to have always same number of chars in all lines. + * Workaround necessary to display correctly aligned when Qt::AlignRight. + */ + int dumpAsciiFixedLen = ((m_format & MDTsizemask) == MDTbyte) ? 8:16; + if (dumpAscii.size() < dumpAsciiFixedLen) { + dumpAscii += QString().sprintf("%*c", dumpAsciiFixedLen- dumpAscii.size(), ' '); + } + line->setText(COL_DUMP_ASCII, dumpAscii); line->setTextAlignment(COL_DUMP_ASCII, Qt::AlignRight); - if (fm.width(dumpAscii) > maxWidthAscii) { - maxWidthAscii = fm.width(dumpAscii); + if (fm.width(dumpAscii) > m_memoryColumnsWidth[COL_DUMP_ASCII]) { + m_memoryColumnsWidth[COL_DUMP_ASCII] = fm.width(dumpAscii); } } - - QStringList tmplist; - QMap::Iterator pos = m_old_memory.find( addr ); - - if( pos != m_old_memory.end() ) - tmplist = pos.value().split( "\t" ); - - for (int i = 0; i < sl.count(); i++) - { - bool changed = - i < tmplist.count() && - sl[i] != tmplist[i]; - line->setForeground(i+1, changed ? QBrush(QColor(Qt::red)) : palette().text()); - } } - // resize to longest string and add padding to addr/dumpascii columns + // resize to longest string and add padding int padding = 25; - m_memory.header()->resizeSection(COL_ADDR, maxWidthAddr + padding); - m_memory.header()->resizeSection(COL_DUMP_ASCII, maxWidthAscii + padding); - - m_old_memory.clear(); - m_old_memory = tmpMap; + m_memory.header()->resizeSection(COL_ADDR, m_memoryColumnsWidth[COL_ADDR] + padding); + m_memory.header()->resizeSection(COL_DUMP_ASCII, m_memoryColumnsWidth[COL_DUMP_ASCII] + padding); + for (int i = 1; i < COL_DUMP_ASCII; i++) { + m_memory.header()->resizeSection(i, m_memoryColumnsWidth[i] + 10); + } } static const char MemoryGroup[] = "Memory"; diff --git a/kdbg/memwindow.h b/kdbg/memwindow.h index 7039d358..8beaf64c 100644 --- a/kdbg/memwindow.h +++ b/kdbg/memwindow.h @@ -31,10 +31,14 @@ class MemoryWindow : public QWidget QComboBox m_expression; QTreeWidget m_memory; - QMap m_old_memory; + QList m_memoryColumnsWidth; + int m_memoryRowHeight = 0; QBoxLayout m_layout; + bool m_dumpMemRegionEnd = false; + DbgAddr m_dumpLastAddr; + unsigned m_dumpLength = 0; unsigned m_format; QMap m_formatCache; @@ -42,9 +46,12 @@ class MemoryWindow : public QWidget virtual void contextMenuEvent(QContextMenuEvent* ev); void displayNewExpression(const QString& expr); + void requestMemoryDump(const QString &expr); QString parseMemoryDumpLineToAscii(const QString& line, bool littleendian); public slots: + void verticalScrollBarMoved(int); + void verticalScrollBarRangeChanged(int, int); void slotNewExpression(const QString&); void slotNewExpression(); void slotTypeChange(QAction*); diff --git a/kdbg/xsldbgdriver.cpp b/kdbg/xsldbgdriver.cpp index 54f6764d..261c79b4 100644 --- a/kdbg/xsldbgdriver.cpp +++ b/kdbg/xsldbgdriver.cpp @@ -515,6 +515,14 @@ XsldbgDriver::makeCmdString(DbgCommand cmd) return cmds[cmd].fmt; } +QString +XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg1, int) +{ + assert(cmd == DCexamine); + + return makeCmdString(cmd, strArg, intArg1); +} + void XsldbgDriver::terminate() { diff --git a/kdbg/xsldbgdriver.h b/kdbg/xsldbgdriver.h index bb683bfb..bdf9aa94 100644 --- a/kdbg/xsldbgdriver.h +++ b/kdbg/xsldbgdriver.h @@ -74,6 +74,7 @@ class XsldbgDriver:public DebuggerDriver { virtual QString makeCmdString(DbgCommand cmd, QString strArg, int intArg); virtual QString makeCmdString(DbgCommand cmd, QString strArg1, QString strArg2); virtual QString makeCmdString(DbgCommand cmd, int intArg1, int intArg2); + virtual QString makeCmdString(DbgCommand cmd, QString strArg, int intArg1, int intArg2); virtual int findPrompt(const QByteArray& output) const; void parseMarker(); };