Merge branch 'memory-dump-dynamic'
[kdbg.git] / kdbg / memwindow.cpp
blob46f1289ba1d79cbf734cc80c7115ff783a86383b
1 /*
2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
5 */
7 #include "memwindow.h"
8 #include <QFontDatabase>
9 #include <QHeaderView>
10 #include <QMouseEvent>
11 #include <QScrollBar>
12 #include <QList>
13 #include <klocale.h>
14 #include <kconfigbase.h>
15 #include <kconfiggroup.h>
16 #include "debugger.h"
18 const int COL_ADDR = 0;
19 const int COL_DUMP_ASCII = 9;
20 const int MAX_COL = 10;
22 MemoryWindow::MemoryWindow(QWidget* parent) :
23 QWidget(parent),
24 m_debugger(0),
25 m_expression(this),
26 m_memory(this),
27 m_layout(QBoxLayout::TopToBottom, this),
28 m_format(MDTword | MDThex)
30 setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
32 m_expression.setEditable(true);
33 QSize minSize = m_expression.sizeHint();
34 m_expression.setMinimumSize(minSize);
35 m_expression.setInsertPolicy(QComboBox::NoInsert);
36 m_expression.setMaxCount(15);
38 m_memory.setColumnCount(10);
39 for (int i = 0; i < MAX_COL; i++) {
40 m_memory.header()->setSectionResizeMode(i, QHeaderView::Fixed);
42 m_memory.header()->setStretchLastSection(false);
44 m_memory.setSortingEnabled(false); /* don't sort */
45 m_memory.setAllColumnsShowFocus(true);
46 m_memory.setRootIsDecorated(false);
47 m_memory.header()->setSectionsClickable(false);
48 m_memory.header()->setSectionsMovable(false); // don't move columns
49 m_memory.setHeaderHidden(true); // hide header
50 m_memory.setContextMenuPolicy(Qt::NoContextMenu); // defer to parent
52 // get row height
53 new QTreeWidgetItem(&m_memory, QStringList("0x179bf"));
54 m_memoryRowHeight = m_memory.visualRect(m_memory.indexAt(QPoint(0, 0))).height();
55 m_memory.clear();
57 // create layout
58 m_layout.setSpacing(2);
59 m_layout.addWidget(&m_expression, 0);
60 m_layout.addWidget(&m_memory, 10);
61 m_layout.activate();
63 connect(&m_expression, SIGNAL(activated(const QString&)),
64 this, SLOT(slotNewExpression(const QString&)));
65 connect(m_expression.lineEdit(), SIGNAL(returnPressed()),
66 this, SLOT(slotNewExpression()));
67 connect(m_memory.verticalScrollBar(), SIGNAL(valueChanged(int)),
68 this, SLOT(verticalScrollBarMoved(int)));
69 connect(m_memory.verticalScrollBar(), SIGNAL(rangeChanged(int, int)),
70 this, SLOT(verticalScrollBarRangeChanged(int, int)));
72 // the popup menu
73 QAction* pAction;
74 pAction = m_popup.addAction(i18n("B&ytes"));
75 pAction->setData(MDTbyte);
76 pAction = m_popup.addAction(i18n("Halfwords (&2 Bytes)"));
77 pAction->setData(MDThalfword);
78 pAction = m_popup.addAction(i18n("Words (&4 Bytes)"));
79 pAction->setData(MDTword);
80 pAction = m_popup.addAction(i18n("Giantwords (&8 Bytes)"));
81 pAction->setData(MDTgiantword);
82 m_popup.addSeparator();
83 pAction = m_popup.addAction(i18n("He&xadecimal"));
84 pAction->setData(MDThex);
85 pAction = m_popup.addAction(i18n("Signed &decimal"));
86 pAction->setData(MDTsigned);
87 pAction = m_popup.addAction(i18n("&Unsigned decimal"));
88 pAction->setData(MDTunsigned);
89 pAction = m_popup.addAction(i18n("&Octal"));
90 pAction->setData(MDToctal);
91 pAction = m_popup.addAction(i18n("&Binary"));
92 pAction->setData(MDTbinary);
93 pAction = m_popup.addAction(i18n("&Addresses"));
94 pAction->setData(MDTaddress);
95 pAction = m_popup.addAction(i18n("&Character"));
96 pAction->setData(MDTchar);
97 pAction = m_popup.addAction(i18n("&Floatingpoint"));
98 pAction->setData(MDTfloat);
99 pAction = m_popup.addAction(i18n("&Strings"));
100 pAction->setData(MDTstring);
101 pAction = m_popup.addAction(i18n("&Instructions"));
102 pAction->setData(MDTinsn);
103 connect(&m_popup, SIGNAL(triggered(QAction*)), this, SLOT(slotTypeChange(QAction*)));
106 MemoryWindow::~MemoryWindow()
110 void MemoryWindow::requestMemoryDump(const QString &expr)
113 * Memory dump same length for all cases
114 * Length of memory dump to request is n_rows (window size / row_height) * column_size
115 * Column size
116 * Byte 8
117 * Halfword 8
118 * Word 4
119 * Giantword 2
120 * Strings and instructions format are column size 1
122 int nrows = m_memory.height() / m_memoryRowHeight;
123 unsigned int colum_size = 1;
124 unsigned int format = m_format & MDTformatmask;
126 if (m_dumpMemRegionEnd) {
127 return;
130 /* Move to driver ???? */
131 if (!(format == MDTinsn || format == MDTstring)) {
132 switch(m_format & MDTsizemask) {
133 case MDTbyte:
134 case MDThalfword:
135 colum_size= 8;
136 break;
137 case MDTword:
138 colum_size= 4;
139 break;
140 case MDTgiantword:
141 colum_size= 2;
142 break;
145 unsigned request_length = nrows * colum_size * 2;
146 m_dumpLength += request_length;
147 m_debugger->setMemoryExpression(m_expression.lineEdit()->text(), m_dumpLength, expr, request_length);
150 void MemoryWindow::verticalScrollBarRangeChanged(int min, int max)
152 if (min == 0 && max == 0 && !m_dumpLastAddr.isEmpty()) {
153 requestMemoryDump(m_dumpLastAddr.asString());
157 void MemoryWindow::verticalScrollBarMoved(int value)
159 int scrollmax = m_memory.verticalScrollBar()->maximum();
160 if (value == scrollmax && !m_dumpLastAddr.isEmpty()) {
161 requestMemoryDump(m_dumpLastAddr.asString());
165 void MemoryWindow::contextMenuEvent(QContextMenuEvent* ev)
167 m_popup.popup(ev->globalPos());
168 ev->accept();
171 void MemoryWindow::slotNewExpression()
173 slotNewExpression(m_expression.lineEdit()->text());
176 void MemoryWindow::slotNewExpression(const QString& newText)
178 QString text = newText.simplified();
180 // see if the string is in the list
181 // (note: must count downwards because of removeItem!)
182 for (int i = m_expression.count()-1; i >= 0; i--)
184 if (m_expression.itemText(i) == text) {
185 // yes it is!
186 // look up the format that was used last time for this expr
187 QMap<QString,unsigned>::iterator pFormat = m_formatCache.find(text);
188 if (pFormat != m_formatCache.end()) {
189 m_format = *pFormat;
190 m_debugger->setMemoryFormat(m_format);
192 // remove this text, will be inserted at the top
193 m_expression.removeItem(i);
196 m_expression.insertItem(0, text);
197 m_expression.setCurrentIndex(0);
199 if (!text.isEmpty()) {
200 m_formatCache[text] = m_format;
203 displayNewExpression(text);
206 void MemoryWindow::displayNewExpression(const QString& expr)
208 // Clear variables
209 m_dumpMemRegionEnd = false;
210 m_dumpLastAddr = DbgAddr{};
211 m_dumpLength = 0;
213 if (m_memory.verticalScrollBar())
214 m_memory.verticalScrollBar()->setValue(0);
216 m_memory.clear();
217 m_memoryColumnsWidth.clear();
218 for (int i = 0; i < MAX_COL; i++) {
219 m_memoryColumnsWidth.append(0);
222 requestMemoryDump(expr);
223 m_expression.setEditText(expr);
226 void MemoryWindow::slotTypeChange(QAction* action)
228 int id = action->data().toInt();
230 // compute new type
231 if (id & MDTsizemask)
232 m_format = (m_format & ~MDTsizemask) | id;
233 if (id & MDTformatmask)
234 m_format = (m_format & ~MDTformatmask) | id;
235 m_debugger->setMemoryFormat(m_format);
237 // change the format in the cache
238 QString expr = m_expression.currentText();
239 m_formatCache[expr.simplified()] = m_format;
241 // force redisplay
242 displayNewExpression(expr);
245 QString MemoryWindow::parseMemoryDumpLineToAscii(const QString& line, bool littleendian)
247 QStringList hexdata = line.split("\t");
249 // Get the size of value from hex str representation length
250 // 0x00 = (4 - 2) / 2 -> 1 byte
251 // 0x0000 = (6 - 2) / 2 -> 2 half-word
252 // 0x00000000 = (10 - 2) / 2 -> 4 word
253 // 0x0000000000000000 = (18 - 2) / 2 -> 8 giant word
255 int len = (hexdata[0].length()-2) / 2;
256 QString dumpAscii;
257 for (const auto& hex: hexdata)
259 bool ok;
260 unsigned long long parsedValue = hex.toULongLong(&ok, 16);
261 if (ok) {
262 QByteArray dump;
263 for (int s = 0; s < len; s++)
265 unsigned char b = parsedValue & 0xFF;
266 parsedValue >>= 8;
267 if (isprint(b)) {
268 dump += b;
269 } else {
270 dump += '.';
273 if (!littleendian)
274 std::reverse(dump.begin(), dump.end());
275 dumpAscii += QString::fromLatin1(dump);
278 return dumpAscii;
281 void MemoryWindow::slotNewMemoryDump(const QString& msg, const std::list<MemoryDump>& memdump)
283 QFontMetrics fm(font());
285 if (!msg.isEmpty()) {
286 for (int i = MAX_COL; i > 0; i--) {
287 m_memory.setColumnHidden(i, true);
289 new QTreeWidgetItem(&m_memory, QStringList() << msg);
290 m_memory.header()->resizeSection(COL_ADDR, fm.width(msg)+10);
291 return;
294 bool showDumpAscii = (m_format & MDTformatmask) == MDThex;
296 std::list<MemoryDump>::const_iterator md = memdump.begin();
298 // show only needed columns
299 QStringList sl = md->dump.split( "\t" );
300 for (int i = COL_DUMP_ASCII-1; i > 0; i--)
301 m_memory.setColumnHidden(i, i > sl.count());
303 m_memory.setColumnHidden(COL_DUMP_ASCII, !showDumpAscii);
305 for (; md != memdump.end(); ++md)
307 QString addr = md->address.asString() + " " + md->address.fnoffs;
308 QStringList sl = md->dump.split( "\t" );
310 if (fm.width(addr) > m_memoryColumnsWidth[COL_ADDR]) {
311 m_memoryColumnsWidth[COL_ADDR] = fm.width(addr);
314 QTreeWidgetItem* line = nullptr;
315 QList<QTreeWidgetItem*> items = m_memory.findItems(addr, Qt::MatchExactly, 0);
316 if (items.count() != 0) {
317 line = items.at(0);
319 if (!line) { // line not found in memory view, append new one
320 for (int i = 0; i < sl.count(); i++) {
321 if (fm.width(sl[i]) > m_memoryColumnsWidth[i+1]) {
322 m_memoryColumnsWidth[i+1] = fm.width(sl[i]);
326 line = new QTreeWidgetItem(&m_memory, QStringList(addr) << sl);
327 m_dumpLastAddr = md->address;
328 if (md->endOfDump)
329 m_dumpMemRegionEnd = true;
330 } else { // line found in memory view updated it
331 for (int i = 0; i < sl.count(); i++) {
332 if (fm.width(sl[i]) > m_memoryColumnsWidth[i+1]) {
333 m_memoryColumnsWidth[i+1] = fm.width(sl[i]);
335 bool changed = i < (line->columnCount() - showDumpAscii) && sl[i] != line->text(i+1);
336 line->setForeground(i+1, changed ? QBrush(QColor(Qt::red)) : palette().text());
337 line->setText(i+1, sl[i]);
341 if (showDumpAscii) {
342 QString dumpAscii = parseMemoryDumpLineToAscii(md->dump, md->littleendian);
344 * Add space padding to have always same number of chars in all lines.
345 * Workaround necessary to display correctly aligned when Qt::AlignRight.
347 int dumpAsciiFixedLen = ((m_format & MDTsizemask) == MDTbyte) ? 8:16;
348 if (dumpAscii.size() < dumpAsciiFixedLen) {
349 dumpAscii += QString().sprintf("%*c", dumpAsciiFixedLen- dumpAscii.size(), ' ');
351 line->setText(COL_DUMP_ASCII, dumpAscii);
352 line->setTextAlignment(COL_DUMP_ASCII, Qt::AlignRight);
353 if (fm.width(dumpAscii) > m_memoryColumnsWidth[COL_DUMP_ASCII]) {
354 m_memoryColumnsWidth[COL_DUMP_ASCII] = fm.width(dumpAscii);
359 // resize to longest string and add padding
360 int padding = 25;
361 m_memory.header()->resizeSection(COL_ADDR, m_memoryColumnsWidth[COL_ADDR] + padding);
362 m_memory.header()->resizeSection(COL_DUMP_ASCII, m_memoryColumnsWidth[COL_DUMP_ASCII] + padding);
363 for (int i = 1; i < COL_DUMP_ASCII; i++) {
364 m_memory.header()->resizeSection(i, m_memoryColumnsWidth[i] + 10);
368 static const char MemoryGroup[] = "Memory";
369 static const char NumExprs[] = "NumExprs";
370 static const char ExpressionFmt[] = "Expression%d";
371 static const char FormatFmt[] = "Format%d";
372 static const char ColumnWidths[] = "ColumnWidths";
374 void MemoryWindow::saveProgramSpecific(KConfigBase* config)
376 KConfigGroup g = config->group(MemoryGroup);
378 int numEntries = m_expression.count();
379 g.writeEntry(NumExprs, numEntries);
380 QString exprEntry;
381 QString fmtEntry;
382 for (int i = 0; i < numEntries;) {
383 QString text = m_expression.itemText(i);
384 i++; /* entries are counted 1-based */
385 exprEntry.sprintf(ExpressionFmt, i);
386 fmtEntry.sprintf(FormatFmt, i);
387 g.writeEntry(exprEntry, text);
388 QMap<QString,unsigned>::iterator pFormat = m_formatCache.find(text);
389 unsigned fmt = pFormat != m_formatCache.end() ? *pFormat : MDTword | MDThex;
390 g.writeEntry(fmtEntry, fmt);
393 // column widths
394 QList<int> widths;
395 for (int i = 0; i < 2; i++) {
396 int w = m_memory.columnWidth(i);
397 widths.append(w);
399 g.writeEntry(ColumnWidths, widths);
402 void MemoryWindow::restoreProgramSpecific(KConfigBase* config)
404 KConfigGroup g = config->group(MemoryGroup);
406 int numEntries = g.readEntry(NumExprs, 0);
407 m_expression.clear();
409 QString exprEntry;
410 QString fmtEntry;
411 // entries are counted 1-based
412 for (int i = 1; i <= numEntries; i++) {
413 exprEntry.sprintf(ExpressionFmt, i);
414 fmtEntry.sprintf(FormatFmt, i);
415 QString expr = g.readEntry(exprEntry, QString());
416 unsigned fmt = g.readEntry(fmtEntry, MDTword | MDThex);
417 m_expression.addItem(expr);
418 m_formatCache[expr] = fmt & (MDTsizemask | MDTformatmask);
421 // initialize with top expression
422 if (numEntries > 0) {
423 m_expression.setCurrentIndex(0);
424 QString expr = m_expression.itemText(0);
425 m_format = m_formatCache[expr];
426 m_debugger->setMemoryFormat(m_format);
427 displayNewExpression(expr);
430 // column widths
431 QList<int> widths = g.readEntry(ColumnWidths, QList<int>());
432 QList<int>::iterator w = widths.begin();
433 for (int i = 0; i < 2 && w != widths.end(); ++i, ++w) {
434 m_memory.setColumnWidth(i, *w);
439 #include "memwindow.moc"