debuggerruncontrolfactory.h \
debuggertooltipmanager.h \
debuggertoolchaincombobox.h \
- debuggersourcepathmappingwidget.h
+ debuggersourcepathmappingwidget.h \
+ memoryviewwidget.h
SOURCES += breakhandler.cpp \
breakpoint.cpp \
watchdelegatewidgets.cpp \
debuggertooltipmanager.cpp \
debuggertoolchaincombobox.cpp \
- debuggersourcepathmappingwidget.cpp
+ debuggersourcepathmappingwidget.cpp \
+ memoryviewwidget.cpp
FORMS += attachexternaldialog.ui \
attachcoredialog.ui \
d->m_memoryAgent.createBinEditor(address);
}
+void DebuggerEngine::addMemoryView(Internal::MemoryViewWidget *w)
+{
+ d->m_memoryAgent.addMemoryView(w);
+}
+
void DebuggerEngine::updateMemoryViews()
{
d->m_memoryAgent.updateContents();
class BreakpointParameters;
class QmlCppEngine;
class DebuggerToolTipContext;
+class MemoryViewWidget;
struct WatchUpdateFlags
{
virtual void watchPoint(const QPoint &);
virtual void openMemoryView(quint64 addr);
+ virtual void addMemoryView(Internal::MemoryViewWidget *w);
virtual void fetchMemory(Internal::MemoryAgent *, QObject *,
quint64 addr, quint64 length);
virtual void changeMemory(Internal::MemoryAgent *, QObject *,
#include "memoryagent.h"
#include "debuggerengine.h"
+#include "debuggerstartparameters.h"
#include "debuggercore.h"
+#include "memoryviewwidget.h"
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <utils/qtcassert.h>
#include <QtGui/QMessageBox>
+#include <QtGui/QMainWindow>
+
+#include <cstring>
using namespace Core;
EditorManager::instance()->closeEditors(editors);
}
+void MemoryAgent::openMemoryView(quint64 address, quint64 length, const QPoint &pos)
+{
+ MemoryViewWidget *w = new MemoryViewWidget(Core::ICore::instance()->mainWindow());
+ w->setUpdateOnInferiorStop(true);
+ w->move(pos);
+ w->requestMemory(address, length);
+ addMemoryView(w);
+}
+
+void MemoryAgent::addMemoryView(MemoryViewWidget *w)
+{
+ w->setAbi(m_engine->startParameters().toolChainAbi);
+ connect(w, SIGNAL(memoryRequested(quint64,quint64)),
+ this, SLOT(updateMemoryView(quint64,quint64)));
+ connect(m_engine, SIGNAL(stateChanged(Debugger::DebuggerState)),
+ w, SLOT(engineStateChanged(Debugger::DebuggerState)));
+ connect(w, SIGNAL(openViewRequested(quint64,quint64,QPoint)),
+ this, SLOT(openMemoryView(quint64,quint64,QPoint)));
+ w->requestMemory();
+ w->show();
+}
+
+void MemoryAgent::updateMemoryView(quint64 address, quint64 length)
+{
+ m_engine->fetchMemory(this, sender(), address, length);
+}
+
void MemoryAgent::createBinEditor(quint64 addr)
{
EditorManager *editorManager = EditorManager::instance();
void MemoryAgent::addLazyData(QObject *editorToken, quint64 addr,
const QByteArray &ba)
{
- IEditor *editor = qobject_cast<IEditor *>(editorToken);
- if (editor && editor->widget()) {
- QMetaObject::invokeMethod(editor->widget(), "addData",
- Q_ARG(quint64, addr / BinBlockSize), Q_ARG(QByteArray, ba));
+
+ if (IEditor *editor = qobject_cast<IEditor *>(editorToken)) {
+ if (QWidget *editorWidget = editor->widget()) {
+ QMetaObject::invokeMethod(editorWidget , "addData",
+ Q_ARG(quint64, addr / BinBlockSize), Q_ARG(QByteArray, ba));
+ }
+ return;
}
+ if (MemoryViewWidget *mvw = qobject_cast<MemoryViewWidget*>(editorToken))
+ mvw->setData(ba);
}
void MemoryAgent::provideNewRange(IEditor *editor, quint64 address)
return false;
}
+bool MemoryAgent::isBigEndian(const ProjectExplorer::Abi &a)
+{
+ switch (a.architecture()) {
+ case ProjectExplorer::Abi::UnknownArchitecture:
+ case ProjectExplorer::Abi::X86Architecture:
+ case ProjectExplorer::Abi::ItaniumArchitecture: // Configureable
+ case ProjectExplorer::Abi::ArmArchitecture: // Configureable
+ break;
+ case ProjectExplorer::Abi::MipsArcitecture: // Configureable
+ case ProjectExplorer::Abi::PowerPCArchitecture: // Configureable
+ return true;
+ }
+ return false;
+}
+
+// Read a POD variable from a memory location. Swap bytes if endianness differs
+template <class POD> POD readPod(const unsigned char *data, bool swapByteOrder)
+{
+ POD pod = 0;
+ if (swapByteOrder) {
+ unsigned char *target = reinterpret_cast<unsigned char *>(&pod) + sizeof(POD) - 1;
+ for (size_t i = 0; i < sizeof(POD); i++)
+ *target-- = data[i];
+ } else {
+ std::memcpy(&pod, data, sizeof(POD));
+ }
+ return pod;
+}
+
+// Read memory from debuggee
+quint64 MemoryAgent::readInferiorPointerValue(const unsigned char *data, const ProjectExplorer::Abi &a)
+{
+ const bool swapByteOrder = isBigEndian(a) != isBigEndian(ProjectExplorer::Abi::hostAbi());
+ return a.wordWidth() == 32 ? readPod<quint32>(data, swapByteOrder) :
+ readPod<quint64>(data, swapByteOrder);
+}
+
} // namespace Internal
} // namespace Debugger
#include <QtCore/QObject>
#include <QtCore/QPointer>
+QT_FORWARD_DECLARE_CLASS(QPoint)
+
namespace Core {
class IEditor;
}
+namespace ProjectExplorer {
+class Abi;
+}
+
namespace Debugger {
class DebuggerEngine;
namespace Internal {
+class MemoryViewWidget;
class MemoryAgent : public QObject
{
enum { BinBlockSize = 1024 };
bool hasVisibleEditor() const;
+ static bool isBigEndian(const ProjectExplorer::Abi &a);
+ static quint64 readInferiorPointerValue(const unsigned char *data, const ProjectExplorer::Abi &a);
+
public slots:
// Called by engine to create a new view.
void createBinEditor(quint64 startAddr);
+ // Called by engine to create a tooltip.
+ void addMemoryView(MemoryViewWidget *w);
// Called by engine to trigger update of contents.
void updateContents();
// Called by engine to pass updated contents.
void handleEndOfFileRequested(Core::IEditor *editor);
void handleDataChanged(Core::IEditor *editor, quint64 address,
const QByteArray &data);
+ void updateMemoryView(quint64 address, quint64 length);
+ void openMemoryView(quint64 address, quint64 length, const QPoint &pos);
private:
QList<QPointer<Core::IEditor> > m_editors;
--- /dev/null
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** No Commercial Usage
+**
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "memoryviewwidget.h"
+#include "memoryagent.h"
+#include "registerhandler.h"
+
+#include <coreplugin/coreconstants.h>
+#include <texteditor/fontsettings.h>
+
+#include <QtGui/QLabel>
+#include <QtGui/QVBoxLayout>
+#include <QtGui/QPlainTextEdit>
+#include <QtGui/QScrollBar>
+#include <QtGui/QToolButton>
+#include <QtGui/QToolBar>
+#include <QtGui/QTextCursor>
+#include <QtGui/QTextBlock>
+#include <QtGui/QTextDocument>
+#include <QtGui/QIcon>
+#include <QtGui/QFont>
+#include <QtGui/QFontMetrics>
+#include <QtGui/QMenu>
+
+#include <QtCore/QTextStream>
+#include <QtCore/QDebug>
+#include <QtCore/QVariant>
+
+#include <cctype>
+
+enum { debug = 0 };
+
+// Formatting: 'aaaa:aaaa 0f ab... ASC..'
+enum
+{
+ bytesPerLine = 16,
+ lineWidth = 11 + 4 * bytesPerLine
+};
+
+namespace Debugger {
+namespace Internal {
+
+/*!
+ \class Debugger::Internal::MemoryViewWidget
+ \brief Base class for memory view tool windows
+
+ Small tool-window that stays on top and displays a chunk of memory.
+ Provides next/previous browsing.
+
+ Constructed by passing an instance to \c DebuggerEngine::addMemoryView()
+ which will pass it on to \c Debugger::Internal::MemoryAgent::addMemoryView()
+ to set up the signal connections to the engine.
+
+ Provides API for marking text with a special format/color.
+ The formatting is stored as a list of struct MemoryViewWidget::Markup and applied
+ by converting into extra selections once data arrives in setData().
+
+ Provides a context menu that offers to open a subview from a pointer value
+ obtained from the memory shown (converted using the Abi).
+
+ \sa Debugger::Internal::MemoryAgent, Debugger::DebuggerEngine
+ \sa ProjectExplorer::Abi
+*/
+
+const quint64 MemoryViewWidget::defaultLength = 128;
+
+MemoryViewWidget::MemoryViewWidget(QWidget *parent) :
+ QWidget(parent, Qt::Tool|Qt::WindowStaysOnTopHint),
+ m_previousButton(new QToolButton),
+ m_nextButton(new QToolButton),
+ m_textEdit(new QPlainTextEdit),
+ m_content(new QLabel),
+ m_address(0),
+ m_length(0),
+ m_requestedAddress(0),
+ m_requestedLength(0),
+ m_updateOnInferiorStop(false)
+{
+ setAttribute(Qt::WA_DeleteOnClose);
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ QToolBar *toolBar = new QToolBar;
+ toolBar->setObjectName(QLatin1String("MemoryViewWidgetToolBar"));
+ toolBar->setProperty("_q_custom_style_disabled", QVariant(true));
+ toolBar->setIconSize(QSize(16, 16));
+ m_previousButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_PREV)));
+ connect(m_previousButton, SIGNAL(clicked()), this, SLOT(slotPrevious()));
+ toolBar->addWidget(m_previousButton);
+ m_nextButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_NEXT)));
+ connect(m_nextButton, SIGNAL(clicked()), this, SLOT(slotNext()));
+ toolBar->addWidget(m_nextButton);
+
+ layout->addWidget(toolBar);
+ m_textEdit->setObjectName(QLatin1String("MemoryViewWidgetTextEdit"));
+ m_textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ m_textEdit->setReadOnly(true);
+ m_textEdit->setWordWrapMode(QTextOption::NoWrap);
+ m_textEdit->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(m_textEdit, SIGNAL(customContextMenuRequested(QPoint)),
+ this, SLOT(slotContextMenuRequested(QPoint)));
+ // Text: Pick a fixed font and set minimum size to accommodate default length with vertical scrolling
+ const QFont fixedFont(TextEditor::FontSettings::defaultFixedFontFamily(), TextEditor::FontSettings::defaultFontSize());
+ const QFontMetrics metrics(fixedFont);
+ const QSize lineSize = metrics.size(Qt::TextSingleLine , QString(lineWidth, QLatin1Char('0')));
+ int defaultLineCount = defaultLength / bytesPerLine;
+ if (defaultLength % bytesPerLine)
+ defaultLineCount++;
+ const QSize textSize(lineSize.width() + m_textEdit->verticalScrollBar()->width() + 10,
+ lineSize.height() * defaultLineCount + 10);
+ m_textEdit->setFont(fixedFont);
+ m_textEdit->setMinimumSize(textSize);
+ m_textEdit->installEventFilter(this);
+ layout->addWidget(m_textEdit);
+}
+
+void MemoryViewWidget::engineStateChanged(Debugger::DebuggerState s)
+{
+ switch (s) {
+ case Debugger::InferiorUnrunnable:
+ setBrowsingEnabled(true);
+ break;
+ case Debugger::InferiorStopOk:
+ setBrowsingEnabled(true);
+ if (m_updateOnInferiorStop)
+ requestMemory();
+ break;
+ case Debugger::DebuggerFinished:
+ close();
+ break;
+ default:
+ setBrowsingEnabled(false);
+ break;
+ }
+}
+
+void MemoryViewWidget::setBrowsingEnabled(bool b)
+{
+ m_previousButton->setEnabled(b && m_address >= m_length);
+ m_nextButton->setEnabled(b);
+}
+
+void MemoryViewWidget::clear()
+{
+ m_data.clear();
+ m_textEdit->setExtraSelections(QList<QTextEdit::ExtraSelection>());
+ m_textEdit->setPlainText(tr("No data available."));
+ setBrowsingEnabled(false);
+ updateTitle();
+}
+
+void MemoryViewWidget::requestMemory()
+{
+ requestMemory(m_address, m_length);
+}
+
+void MemoryViewWidget::requestMemory(quint64 address, quint64 length)
+{
+ m_requestedAddress = address;
+ m_requestedLength = length;
+
+ // For RegisterMemoryViewWidget, the register values sometimes switch to 0
+ // while stepping, handle gracefully.
+ if (!address || !length) {
+ m_address = address;
+ m_length = length;
+ clear();
+ return;
+ }
+
+ // Is this the first request and no data available yet? -> Set initial state.
+ if (m_data.isEmpty() && !m_address && !m_length) {
+ m_address = address;
+ m_length = length;
+ updateTitle();
+ m_textEdit->setPlainText(tr("Fetching %1 bytes...").arg(length));
+ setBrowsingEnabled(false);
+ }
+ if (debug)
+ qDebug() << this << "requestMemory()" << m_requestedAddress << m_requestedLength
+ << " currently at: " << m_address << m_length;
+ emit memoryRequested(m_requestedAddress, m_requestedLength);
+}
+
+void MemoryViewWidget::setTitle(const QString &t)
+{
+ setWindowTitle(t);
+}
+
+void MemoryViewWidget::slotNext()
+{
+ requestMemory(m_address + m_length, m_length);
+}
+
+void MemoryViewWidget::slotPrevious()
+{
+ if (m_address >= m_length)
+ requestMemory(m_address - m_length, m_length);
+}
+
+// Convert address to line and column in range 0..(n - 1), return false
+// if out of range.
+bool MemoryViewWidget::addressToLineColumn(quint64 posAddress,
+ int *lineIn /* = 0 */, int *columnIn /* = 0 */,
+ quint64 *lineStartIn /* = 0 */) const
+{
+ if (posAddress < m_address)
+ return false;
+ const quint64 offset = posAddress - m_address;
+ if (offset >= quint64(m_data.size()))
+ return false;
+ const quint64 line = offset / bytesPerLine;
+ const quint64 lineStart = m_address + line * bytesPerLine;
+ if (lineStartIn)
+ *lineStartIn = lineStart;
+ if (lineIn)
+ *lineIn = int(line);
+ const int column = 3 * int(offset % bytesPerLine) + 10;
+ if (columnIn)
+ *columnIn = column;
+ if (debug)
+ qDebug() << this << "at" << m_address << " addressToLineColumn "
+ << posAddress << "->" << line << ',' << column << " lineAt" << lineStart;
+ return true;
+}
+
+// Return address at position
+quint64 MemoryViewWidget::addressAt(const QPoint &textPos) const
+{
+ QTextCursor cursor = m_textEdit->cursorForPosition(textPos);
+ if (cursor.isNull())
+ return 0;
+ const int line = cursor.blockNumber();
+ const int column = cursor.columnNumber() - 1;
+ const quint64 lineAddress = m_address + line * bytesPerLine;
+ const int byte = (qMax(0, column - 9)) / 3;
+ if (byte >= bytesPerLine) // Within ASC part
+ return 0;
+ return lineAddress + byte;
+}
+
+void MemoryViewWidget::slotContextMenuRequested(const QPoint &pos)
+{
+ QMenu *menu = m_textEdit->createStandardContextMenu();
+ menu->addSeparator();
+ // Add action offering to open a sub-view with a pointer read from the memory
+ // at the location: Dereference the chunk of memory as pointer address.
+ QAction *derefPointerAction = 0;
+ quint64 pointerValue = 0;
+ if (!m_data.isEmpty()) {
+ const quint64 pointerSize = m_abi.wordWidth() / 8;
+ quint64 contextAddress = addressAt(pos);
+ if (const quint64 remainder = contextAddress % pointerSize) // Pad pointer location.
+ contextAddress -= remainder;
+ // Dereference pointer from location
+ if (contextAddress) {
+ const quint64 dataOffset = contextAddress - address();
+ if (pointerSize && (dataOffset + pointerSize) <= quint64(m_data.size())) {
+ const unsigned char *data = reinterpret_cast<const unsigned char *>(m_data.constData() + dataOffset);
+ pointerValue = MemoryAgent::readInferiorPointerValue(data, m_abi);
+ }
+ }
+ } // has data
+ if (pointerValue) {
+ const QString msg = tr("Open Memory View at Pointer Value 0x%1")
+ .arg(pointerValue, 0, 16);
+ derefPointerAction = menu->addAction(msg);
+ } else {
+ derefPointerAction = menu->addAction("Open Memory View at Pointer Value");
+ derefPointerAction->setEnabled(false);
+ }
+ const QPoint globalPos = m_textEdit->mapToGlobal(pos);
+ QAction *action = menu->exec(globalPos);
+ if (!action)
+ return;
+ if (action == derefPointerAction) {
+ emit openViewRequested(pointerValue, MemoryViewWidget::defaultLength, globalPos);
+ return;
+ }
+}
+
+// Format address as in binary editor '0000:00AB' onto a stream set up for hex output.
+static inline void formatAddressToHexStream(QTextStream &hexStream, quint64 address)
+{
+ hexStream.setFieldWidth(4);
+ hexStream << (address >> 32);
+ hexStream.setFieldWidth(1);
+ hexStream << ':';
+ hexStream.setFieldWidth(4);
+ hexStream << (address & 0xFFFF);
+}
+
+// Return formatted address for window title: Prefix + binary editor format: '0x0000:00AB'
+static inline QString formattedAddress(quint64 a)
+{
+ QString rc = QLatin1String("0x");
+ QTextStream str(&rc);
+ str.setIntegerBase(16);
+ str.setPadChar(QLatin1Char('0'));
+ formatAddressToHexStream(str, a);
+ return rc;
+}
+
+// Format data as in binary editor '0000:00AB 0A A3 ..ccc'
+QString MemoryViewWidget::formatData(quint64 startAddress, const QByteArray &data)
+{
+ QString rc;
+ rc.reserve(5 * data.size());
+ const quint64 endAddress = startAddress + data.size();
+ QTextStream str(&rc);
+ str.setIntegerBase(16);
+ str.setPadChar(QLatin1Char('0'));
+ str.setFieldAlignment(QTextStream::AlignRight);
+ for (quint64 address = startAddress; address < endAddress; address += 16) {
+ formatAddressToHexStream(str, address);
+ // Format hex bytes
+ const int dataStart = int(address - startAddress);
+ const int dataEnd = qMin(dataStart + int(bytesPerLine), data.size());
+ for (int i = dataStart; i < dataEnd; i++) {
+ str.setFieldWidth(1);
+ str << ' ';
+ str.setFieldWidth(2);
+ const char c = data.at(i);
+ unsigned char uc = c;
+ str << unsigned(uc);
+ }
+ // Pad for character part
+ str.setFieldWidth(1);
+ if (const int remainder = int(bytesPerLine) - (dataEnd - dataStart))
+ str << QString(3 * remainder, QLatin1Char(' '));
+ // Format characters
+ str << ' ';
+ for (int i = dataStart; i < dataEnd; i++) {
+ const char c = data.at(i);
+ str << (c >= 0 && std::isprint(c) ? c : '.'); // MSVC has an assert on c>=0.
+ }
+ str << '\n';
+ }
+ return rc;
+}
+
+void MemoryViewWidget::updateTitle()
+{
+ const QString title = tr("Memory at %1").arg(formattedAddress(address()));
+ setTitle(title);
+}
+
+// Convert an markup range into a list of selections for the bytes,
+// resulting in a rectangular selection in the bytes area (provided range
+// is within data available).
+bool MemoryViewWidget::markUpToSelections(const Markup &r,
+ QList<QTextEdit::ExtraSelection> *extraSelections) const
+{
+ // Fully covered?
+ if (r.address < m_address)
+ return false;
+ const quint64 rangeEnd = r.address + r.size;
+ if (rangeEnd > (m_address + quint64(m_data.size())))
+ return false;
+
+ QTextCursor cursor = m_textEdit->textCursor();
+ cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
+
+ // Goto first position
+ int line;
+ int column;
+ quint64 lineStartAddress;
+
+ if (!addressToLineColumn(r.address, &line, &column, &lineStartAddress))
+ return false;
+
+ if (line)
+ cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, line);
+ if (column)
+ cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, column - 1);
+
+ quint64 current = r.address;
+ // Mark rectangular area in the bytes section
+ while (true) {
+ // Mark in current line
+ quint64 nextLineAddress = lineStartAddress + bytesPerLine;
+ const int numberOfCharsToMark = 3 * int(qMin(nextLineAddress, rangeEnd) - current);
+ cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, numberOfCharsToMark);
+ QTextEdit::ExtraSelection sel;
+ sel.cursor = cursor;
+ sel.format = r.format;
+ extraSelections->push_back(sel);
+ if (nextLineAddress >= rangeEnd)
+ break;
+ // Goto beginning of next line, past address.
+ cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
+ cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor);
+ cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 9);
+ lineStartAddress += bytesPerLine;
+ current = lineStartAddress;
+ }
+ return true;
+}
+
+void MemoryViewWidget::clearMarkup()
+{
+ m_markup.clear();
+ m_textEdit->setExtraSelections(QList<QTextEdit::ExtraSelection>());
+}
+
+void MemoryViewWidget::addMarkup(quint64 begin, quint64 size,
+ const QTextCharFormat &fmt, const QString &toolTip)
+{
+ m_markup.push_back(Markup(begin, size, fmt, toolTip));
+}
+
+void MemoryViewWidget::addMarkup(quint64 begin, quint64 size,
+ const QColor &background, const QString &toolTip)
+{
+ QTextCharFormat format = textCharFormat();
+ format.setBackground(QBrush(background));
+ addMarkup(begin, size, format, toolTip);
+}
+
+QTextCharFormat MemoryViewWidget::textCharFormat() const
+{
+ return m_textEdit->currentCharFormat();
+}
+
+void MemoryViewWidget::setData(const QByteArray &a)
+{
+ if (debug)
+ qDebug() << this << m_requestedAddress << m_requestedLength << "received" << a.size();
+
+ if (quint64(a.size()) < m_requestedLength) {
+ const QString msg = QString::fromLatin1("Warning: %1 received only %2 bytes of %3 at 0x%4")
+ .arg(QString::fromAscii(metaObject()->className()))
+ .arg(a.size()).arg(m_requestedLength).arg(m_requestedAddress, 0, 16);
+ qWarning("%s", qPrintable(msg));
+ }
+
+ if (m_address != m_requestedAddress || m_length != m_requestedLength) {
+ m_address = m_requestedAddress;
+ m_length = m_requestedLength;
+ updateTitle();
+ }
+
+ if (a.isEmpty()) {
+ clear();
+ return;
+ }
+
+ m_data = a;
+
+ QList<QTextEdit::ExtraSelection> extra;
+ m_textEdit->setExtraSelections(extra);
+ m_textEdit->setPlainText(MemoryViewWidget::formatData(address(), a));
+ // Do markup which is in visible range now.
+ foreach (const Markup &r, m_markup)
+ markUpToSelections(r, &extra);
+ if (!extra.isEmpty())
+ m_textEdit->setExtraSelections(extra);
+ setBrowsingEnabled(true);
+}
+
+// Find markup by address.
+int MemoryViewWidget::indexOfMarkup(quint64 address) const
+{
+ const int size = m_markup.size();
+ for (int m = 0; m < size; m++)
+ if (m_markup.at(m).covers(address))
+ return m;
+ return -1;
+}
+
+bool MemoryViewWidget::eventFilter(QObject *o, QEvent *e)
+{
+ if (o != m_textEdit || e->type() != QEvent::ToolTip)
+ return QWidget::eventFilter(o, e);
+ // ToolTip handling: is the cursor over an address range that has a tooltip
+ // defined in the markup list?
+ const QHelpEvent *he = static_cast<const QHelpEvent *>(e);
+ if (const quint64 toolTipAddress = addressAt(he->pos())) {
+ const int mIndex = indexOfMarkup(toolTipAddress);
+ if (mIndex != -1) {
+ m_textEdit->setToolTip(m_markup.at(mIndex).toolTip);
+ } else {
+ m_textEdit->setToolTip(QString());
+ }
+ }
+ return QWidget::eventFilter(o, e);
+}
+
+/*!
+ \class Debugger::Internal::LocalsMemoryViewWidget
+ \brief Memory view that shows the memory at the location of a local variable.
+
+ Refreshes whenever Debugger::InferiorStopOk is reported.
+
+ \sa Debugger::Internal::WatchWindow
+ \sa Debugger::Internal::MemoryAgent, Debugger::DebuggerEngine
+*/
+
+LocalsMemoryViewWidget::LocalsMemoryViewWidget(QWidget *parent) :
+ MemoryViewWidget(parent), m_variableAddress(0)
+{
+ setUpdateOnInferiorStop(true);
+}
+
+void LocalsMemoryViewWidget::init(quint64 variableAddress, quint64 size, const QString &name)
+{
+ m_variableAddress = variableAddress;
+ m_variableSize = size;
+ m_variableName = name;
+ // Size may be 0.
+ addMarkup(variableAddress, qMax(size, quint64(1)), Qt::lightGray);
+ requestMemory(m_variableAddress, qMax(size, quint64(defaultLength)));
+ if (debug)
+ qDebug() << this << "init" << variableAddress << m_variableName << m_variableSize;
+}
+
+void LocalsMemoryViewWidget::updateTitle()
+{
+ const QString variableAddress = formattedAddress(m_variableAddress);
+ if (address() == m_variableAddress) {
+ const QString title = tr("Memory at '%1' (%2)")
+ .arg(m_variableName, variableAddress);
+ setTitle(title);
+ } else if (address() > m_variableAddress) {
+ const QString title = tr("Memory at '%1' (%2 + %3)")
+ .arg(m_variableName, variableAddress)
+ .arg(address() - m_variableAddress);
+ setTitle(title);
+ } else if (address() < m_variableAddress) {
+ const QString title = tr("Memory at '%1' (%2 - %3)")
+ .arg(m_variableName, variableAddress)
+ .arg(m_variableAddress - address());
+ setTitle(title);
+ }
+}
+
+/*!
+ \class Debugger::Internal::RegisterMemoryViewWidget
+ \brief Memory view that shows the memory around the contents of a register
+ (such as stack pointer, program counter),
+ tracking changes of the register value.
+
+ Connects to Debugger::Internal::RegisterHandler to listen for changes
+ of the register value.
+
+ \sa Debugger::Internal::RegisterHandler, Debugger::Internal::RegisterWindow
+ \sa Debugger::Internal::MemoryAgent, Debugger::DebuggerEngine
+*/
+
+RegisterMemoryViewWidget::Markup::Markup(quint64 a, quint64 s,
+ const QTextCharFormat &fmt, const QString &tt) :
+ address(a), size(s), format(fmt), toolTip(tt)
+{
+}
+
+RegisterMemoryViewWidget::RegisterMemoryViewWidget(QWidget *parent) :
+ MemoryViewWidget(parent),
+ m_registerIndex(-1),
+ m_registerAddress(0),
+ m_offset(0)
+{
+ setUpdateOnInferiorStop(false); // We update on register changed.
+}
+
+void RegisterMemoryViewWidget::updateTitle()
+{
+ const quint64 shownAddress = address() + m_offset;
+ const QString registerAddress = formattedAddress(m_registerAddress);
+ if (shownAddress == m_registerAddress) {
+ const QString title = tr("Memory at Register '%1' (%2)")
+ .arg(m_registerName, registerAddress);
+ setTitle(title);
+ } else if (shownAddress > m_registerAddress) {
+ const QString title = tr("Memory at Register '%1' (%2 + %3)")
+ .arg(m_registerName, registerAddress)
+ .arg(shownAddress - m_registerAddress);
+ setTitle(title);
+ } else if (shownAddress < m_registerAddress) {
+ const QString title = tr("Memory at Register '%1' (%2 - %3)")
+ .arg(m_registerName, registerAddress)
+ .arg(m_registerAddress - shownAddress);
+ setTitle(title);
+ }
+}
+
+void RegisterMemoryViewWidget::setRegisterAddress(quint64 a)
+{
+ if (!a) { // Registers might switch to 0 (for example, 'rsi' while stepping out).
+ m_offset = m_registerAddress = a;
+ requestMemory(0, 0);
+ return;
+ }
+ if (m_registerAddress == a) { // Same value: just re-fetch
+ requestMemory();
+ return;
+ }
+ // Show an area around that register
+ m_registerAddress = a;
+ const quint64 range = MemoryViewWidget::defaultLength / 2;
+ const quint64 end = a + range;
+ const quint64 begin = a >= range ? a - range : 0;
+ m_offset = m_registerAddress - begin;
+ // Mark one byte showing the register
+ clearMarkup();
+ addMarkup(m_registerAddress, 1, Qt::lightGray, tr("Register %1").arg(m_registerName));
+ requestMemory(begin, end - begin);
+}
+
+void RegisterMemoryViewWidget::slotRegisterSet(const QModelIndex &index)
+{
+ if (m_registerIndex != index.row())
+ return;
+ const QVariant newAddressV = index.data(Qt::EditRole);
+ if (newAddressV.type() == QVariant::ULongLong) {
+ if (debug)
+ qDebug() << this << m_registerIndex << m_registerName << "slotRegisterSet" << newAddressV;
+ setRegisterAddress(newAddressV.toULongLong());
+ }
+}
+
+void RegisterMemoryViewWidget::init(int registerIndex, RegisterHandler *h)
+{
+ m_registerIndex = registerIndex;
+ m_registerName = QString::fromAscii(h->registerAt(registerIndex).name);
+ if (debug)
+ qDebug() << this << "init" << registerIndex << m_registerName;
+ // Known issue: CDB might reset the model by changing the special
+ // registers it reports.
+ connect(h, SIGNAL(modelReset()), this, SLOT(close()));
+ connect(h, SIGNAL(registerSet(QModelIndex)),
+ this, SLOT(slotRegisterSet(QModelIndex)));
+ setRegisterAddress(h->registerAt(m_registerIndex).editValue().toULongLong());
+}
+
+} // namespace Internal
+} // namespace Debugger
--- /dev/null
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** No Commercial Usage
+**
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef MEMORYTOOLTIP_H
+#define MEMORYTOOLTIP_H
+
+#include "debuggerconstants.h"
+
+#include <projectexplorer/abi.h>
+
+#include <QtGui/QTextEdit> // QTextEdit::ExtraSelection
+#include <QtCore/QList>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+class QModelIndex;
+class QPlainTextEdit;
+class QToolButton;
+class QTextCharFormat;
+QT_END_NAMESPACE
+
+namespace Debugger {
+class DebuggerEngine;
+namespace Internal {
+class RegisterHandler;
+
+// Documentation inside.
+class MemoryViewWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ // Address range to be marked with special format
+ struct Markup
+ {
+ Markup(quint64 a = 0, quint64 s = 0,
+ const QTextCharFormat &fmt = QTextCharFormat(),
+ const QString &toolTip = QString());
+ bool covers(quint64 a) const { return a >= address && a < (address + size); }
+
+ quint64 address;
+ quint64 size;
+ QTextCharFormat format;
+ QString toolTip;
+ };
+
+ explicit MemoryViewWidget(QWidget *parent = 0);
+
+ quint64 address() const { return m_address; }
+ quint64 length() const { return m_length; }
+
+ // How read an address used for 'dereference pointer at' context menu action
+ void setAbi(const ProjectExplorer::Abi &a) { m_abi = a; }
+ ProjectExplorer::Abi abi() const { return m_abi; }
+
+ bool updateOnInferiorStop() const { return m_updateOnInferiorStop; }
+ void setUpdateOnInferiorStop(bool v) { m_updateOnInferiorStop = v ; }
+
+ QTextCharFormat textCharFormat() const;
+
+ QList<Markup> markup() const { return m_markup; }
+ void setMarkup(const QList<Markup> &m) { clearMarkup(); m_markup = m; }
+
+ static QString formatData(quint64 address, const QByteArray &d);
+
+ static const quint64 defaultLength;
+
+ virtual bool eventFilter(QObject *, QEvent *);
+
+signals:
+ // Fetch memory and use setData().
+ void memoryRequested(quint64 address, quint64 length);
+ // Open a (sub) view from context menu
+ void openViewRequested(quint64 address, quint64 length, const QPoint &pos);
+
+public slots:
+ void setData(const QByteArray &a); // Set to empty to indicate non-available data
+ void engineStateChanged(Debugger::DebuggerState s);
+ void addMarkup(quint64 begin, quint64 size, const QTextCharFormat &,
+ const QString &toolTip = QString());
+ void addMarkup(quint64 begin, quint64 size, const QColor &background,
+ const QString &toolTip = QString());
+ void clear();
+ void clearMarkup();
+ void requestMemory();
+ void requestMemory(quint64 address, quint64 length);
+
+protected:
+ virtual void updateTitle();
+ void setTitle(const QString &);
+
+private slots:
+ void slotNext();
+ void slotPrevious();
+ void slotContextMenuRequested(const QPoint &pos);
+
+private:
+ void setBrowsingEnabled(bool);
+ quint64 addressAt(const QPoint &textPos) const;
+ bool addressToLineColumn(quint64 address, int *line = 0, int *column = 0,
+ quint64 *lineStart = 0) const;
+ bool markUpToSelections(const Markup &r,
+ QList<QTextEdit::ExtraSelection> *extraSelections) const;
+ int indexOfMarkup(quint64 address) const;
+
+ QToolButton *m_previousButton;
+ QToolButton *m_nextButton;
+ QPlainTextEdit *m_textEdit;
+ QLabel *m_content;
+ quint64 m_address;
+ quint64 m_length;
+ quint64 m_requestedAddress;
+ quint64 m_requestedLength;
+ ProjectExplorer::Abi m_abi;
+ QByteArray m_data;
+ bool m_updateOnInferiorStop;
+ QList<Markup> m_markup;
+};
+
+class LocalsMemoryViewWidget : public MemoryViewWidget
+{
+ Q_OBJECT
+public:
+ explicit LocalsMemoryViewWidget(QWidget *parent = 0);
+ void init(quint64 variableAddress, quint64 size, const QString &name);
+
+private:
+ virtual void updateTitle();
+
+ quint64 m_variableAddress;
+ quint64 m_variableSize;
+ QString m_variableName;
+};
+
+class RegisterMemoryViewWidget : public MemoryViewWidget
+{
+ Q_OBJECT
+public:
+ explicit RegisterMemoryViewWidget(QWidget *parent = 0);
+ void init(int registerIndex, RegisterHandler *h);
+
+private slots:
+ void slotRegisterSet(const QModelIndex &);
+
+private:
+ virtual void updateTitle();
+ void setRegisterAddress(quint64 a);
+
+ int m_registerIndex;
+ quint64 m_registerAddress;
+ quint64 m_offset;
+ QString m_registerName;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // MEMORYTOOLTIP_H
}
const int size = m_registers.size();
for (int r = 0; r < size; r++) {
+ const QModelIndex regIndex = index(r, 1);
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].value = registers.at(r).value;
- const QModelIndex regIndex = index(r, 1);
emit dataChanged(regIndex, regIndex);
}
+ emit registerSet(regIndex); // notify attached memory views.
}
}
Q_SLOT void setNumberBase(int base);
int numberBase() const { return m_base; }
+signals:
+ void registerSet(const QModelIndex &r); // Register was set, for memory views
+
private:
void calculateWidth();
int rowCount(const QModelIndex &parent = QModelIndex()) const;
**************************************************************************/
#include "registerwindow.h"
-
+#include "memoryviewwidget.h"
#include "debuggeractions.h"
#include "debuggerconstants.h"
#include "debuggercore.h"
connect(debuggerCore()->action(AlwaysAdjustRegistersColumnWidths),
SIGNAL(toggled(bool)),
SLOT(setAlwaysResizeColumnsToContents(bool)));
-}
-
-void RegisterWindow::resizeEvent(QResizeEvent *ev)
-{
- QTreeView::resizeEvent(ev);
+ setObjectName(QLatin1String("RegisterWindow"));
}
void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev)
menu.addSeparator();
- QModelIndex idx = indexAt(ev->pos());
- QString address = handler->registers().at(idx.row()).value;
- QAction *actShowMemory = menu.addAction(QString());
- if (address.isEmpty()) {
- actShowMemory->setText(tr("Open Memory Editor"));
- actShowMemory->setEnabled(false);
+ const QModelIndex idx = indexAt(ev->pos());
+ if (!idx.isValid())
+ return;
+ const Register &aRegister = handler->registers().at(idx.row());
+ const QVariant addressV = aRegister.editValue();
+ const quint64 address = addressV.type() == QVariant::ULongLong ? addressV.toULongLong() : 0;
+ QAction *actViewMemory = menu.addAction(QString());
+ QAction *actEditMemory = menu.addAction(QString());
+ if (address) {
+ const bool canShow = actionsEnabled && (engineCapabilities & ShowMemoryCapability);
+ 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));
} else {
- actShowMemory->setText(tr("Open Memory Editor at %1").arg(address));
- actShowMemory->setEnabled(actionsEnabled
- && (engineCapabilities & ShowMemoryCapability));
+ actEditMemory->setText(tr("Open Memory Editor"));
+ actViewMemory->setText(tr("Open Memory View at Value of Register"));
+ actEditMemory->setEnabled(false);
}
menu.addSeparator();
menu.addAction(debuggerCore()->action(SettingsDialog));
- QAction *act = menu.exec(ev->globalPos());
+ const QPoint position = ev->globalPos();
+ QAction *act = menu.exec(position);
if (act == actAdjust)
resizeColumnsToContents();
else if (act == actReload)
engine->reloadRegisters();
- else if (act == actShowMemory)
- engine->openMemoryView(address.toULongLong(0, 0));
- else if (act == act16)
+ else if (act == actEditMemory)
+ engine->openMemoryView(address);
+ else if (act == actViewMemory) {
+ RegisterMemoryViewWidget *w = new RegisterMemoryViewWidget(this);
+ w->move(position);
+ w->init(idx.row(), handler);
+ engine->addMemoryView(w);
+ } else if (act == act16)
handler->setNumberBase(16);
else if (act == act10)
handler->setNumberBase(10);
void reloadRegisters();
private:
- void resizeEvent(QResizeEvent *ev);
- void contextMenuEvent(QContextMenuEvent *ev);
+ virtual void contextMenuEvent(QContextMenuEvent *ev);
};
} // namespace Internal
#include "debuggercore.h"
#include "debuggerdialogs.h"
#include "debuggerengine.h"
+#include "debuggerstartparameters.h"
#include "watchdelegatewidgets.h"
#include "watchhandler.h"
#include "debuggertooltipmanager.h"
+#include "memoryviewwidget.h"
#include <utils/qtcassert.h>
#include <utils/savedaction.h>
#include <QtCore/QVariant>
#include <QtGui/QApplication>
+#include <QtGui/QPalette>
#include <QtGui/QClipboard>
#include <QtGui/QContextMenuEvent>
#include <QtGui/QHeaderView>
WatchWindow *m_watchWindow;
};
+// Watch model query helpers.
+static inline quint64 addressOf(const QModelIndex &m)
+ { return m.data(LocalsAddressRole).toULongLong(); }
+static inline quint64 pointerValueOf(const QModelIndex &m)
+ { return m.data(LocalsPointerValueRole).toULongLong(); }
+static inline QString nameOf(const QModelIndex &m)
+ { return m.data().toString(); }
+static inline uint sizeOf(const QModelIndex &m)
+ { return m.data(LocalsSizeRole).toUInt(); }
+
+// Helper functionality to obtain a address-sorted list of member variables
+// of a watch model index and its size. Restrict this to the size passed
+// in since static members can be contained that are in different areas.
+struct MemberVariable
+{
+ MemberVariable(quint64 a = 0, uint s = 0, const QString &n = QString()) :
+ address(a), size(s), name(n) {}
+
+ quint64 address;
+ uint size;
+ QString name;
+};
+
+bool lessThanMV(const MemberVariable &m1, const MemberVariable &m2)
+{
+ return m1.address < m2.address;
+}
+
+static QVector<MemberVariable> sortedMemberVariables(const QModelIndex &m,
+ quint64 start, quint64 end)
+{
+ const int rowCount = m.model()->rowCount(m);
+ if (!rowCount)
+ return QVector<MemberVariable>();
+ QVector<MemberVariable> result;
+ result.reserve(rowCount);
+ for (int r = 0; r < rowCount; r++) {
+ const QModelIndex childIndex = m.child(r, 0);
+ const quint64 childAddress = addressOf(childIndex);
+ const uint childSize = sizeOf(childIndex);
+ if (childAddress && childAddress >= start
+ && (childAddress + childSize) <= end) { // Non-static, within area?
+ result.push_back(MemberVariable(childAddress, childSize, nameOf(childIndex)));
+ }
+ }
+ qStableSort(result.begin(), result.end(), lessThanMV);
+ return result;
+}
+
+/*!
+ \fn variableMemoryMarkup()
+
+ \brief Creates markup for a variable in the memory view.
+
+ Marks the 1st order children with alternating colors in the parent, that is, for
+ \code
+ struct Foo {
+ char c1
+ char c2
+ int x2;
+ }
+ \endcode
+ create something like:
+ \code
+ 0 memberColor1
+ 1 memberColor2
+ 2 base color (padding area of parent)
+ 3 base color
+ 4 member color1
+ ...
+ \endcode
+
+ Fixme: When dereferencing a pointer, the size of the pointee is not
+ known, currently. So, we take an area of 1024 and fill the background
+ with the default color so that just the members are shown
+ (sizeIsEstimate=true). This could be fixed by passing the pointee size
+ as well from the debugger, but would require expensive type manipulation.
+
+ \sa Debugger::Internal::MemoryViewWidget
+*/
+
+typedef QList<MemoryViewWidget::Markup> MemoryViewWidgetMarkup;
+
+static inline MemoryViewWidgetMarkup
+ variableMemoryMarkup(const QModelIndex &m, quint64 address, quint64 size,
+ bool sizeIsEstimate,
+ const QTextCharFormat &defaultFormat,
+ const QColor &defaultBackground)
+{
+ enum { debug = 0 };
+
+ typedef QPair<QColor, QString> ColorNamePair;
+ typedef QVector<ColorNamePair> ColorNameVector;
+
+ MemoryViewWidgetMarkup result;
+ const QVector<MemberVariable> members = sortedMemberVariables(m, address, address + size);
+ // Starting out from base, create an array representing the area filled with base
+ // color. Fill children with alternating member colors,
+ // leaving the padding areas of the parent colored with the base color.
+ if (sizeIsEstimate && members.isEmpty())
+ return result; // Fixme: Exact size not known, no point in filling if no children.
+ const QColor baseColor = sizeIsEstimate ? defaultBackground : Qt::lightGray;
+ const QString name = nameOf(m);
+ ColorNameVector ranges(size, ColorNamePair(baseColor, name));
+ if (!members.isEmpty()) {
+ QColor memberColor1 = QColor(Qt::yellow).lighter();
+ QColor memberColor2 = QColor(Qt::cyan).lighter();
+ for (int m = 0; m < members.size(); m++) {
+ QColor memberColor;
+ if (m & 1) {
+ memberColor = memberColor1;
+ memberColor1 = memberColor1.darker(120);
+ } else {
+ memberColor = memberColor2;
+ memberColor2 = memberColor2.darker(120);
+ }
+ const quint64 childOffset = members.at(m).address - address;
+ const QString toolTip = WatchWindow::tr("%1.%2 at #%3")
+ .arg(name, members.at(m).name).arg(childOffset);
+ qFill(ranges.begin() + childOffset,
+ ranges.begin() + childOffset + members.at(m).size,
+ ColorNamePair(memberColor, toolTip));
+ }
+ }
+
+ if (debug) {
+ QDebug dbg = qDebug().nospace();
+ dbg << name << ' ' << address << ' ' << size << '\n';
+ foreach (const MemberVariable &mv, members)
+ dbg << ' ' << mv.name << ' ' << mv.address << ' ' << mv.size << '\n';
+ QString name;
+ for (unsigned i = 0; i < size; i++)
+ if (name != ranges.at(i).second) {
+ dbg << ",[" << i << ' ' << ranges.at(i).first << ' ' << ranges.at(i).second << ']';
+ name = ranges.at(i).second;
+ }
+ }
+
+ // Condense ranges of identical color into markup ranges.
+ for (unsigned i = 0; i < size; i++) {
+ const ColorNamePair &range = ranges.at(i);
+ if (result.isEmpty() || result.back().format.background().color() != range.first) {
+ QTextCharFormat format = defaultFormat;
+ format.setBackground(QBrush(range.first));
+ result.push_back(MemoryViewWidget::Markup(address + i, 1, format, range.second));
+ } else {
+ result.back().size++;
+ }
+ }
+
+ if (debug) {
+ QDebug dbg = qDebug().nospace();
+ dbg << name << ' ' << address << ' ' << size << '\n';
+ foreach (const MemberVariable &mv, members)
+ dbg << ' ' << mv.name << ' ' << mv.address << ' ' << mv.size << '\n';
+ QString name;
+ for (unsigned i = 0; i < size; i++)
+ if (name != ranges.at(i).second) {
+ dbg << ',' << i << ' ' << ranges.at(i).first << ' ' << ranges.at(i).second;
+ name = ranges.at(i).second;
+ }
+ dbg << '\n';
+ foreach (const MemoryViewWidget::Markup &m, result)
+ dbg << m.address << ' ' << m.size << ' ' << m.toolTip << '\n';
+ }
+
+ return result;
+}
+
+// Convenience to create a memory view of a variable.
+static void addVariableMemoryView(DebuggerEngine *engine,
+ const QModelIndex &m, bool deferencePointer,
+ const QPoint &p, QWidget *parent)
+{
+ const QColor background = parent->palette().color(QPalette::Normal, QPalette::Base);
+ LocalsMemoryViewWidget *w = new LocalsMemoryViewWidget(parent);
+ const quint64 address = deferencePointer ? pointerValueOf(m) : addressOf(m);
+ // Fixme: Get the size of pointee (see variableMemoryMarkup())?
+ // Also, gdb does not report the size yet as of 8.4.2011
+ const quint64 typeSize = sizeOf(m);
+ const bool sizeIsEstimate = deferencePointer || !typeSize;
+ const quint64 size = sizeIsEstimate ? 1024 : typeSize;
+ if (!address)
+ return;
+ const MemoryViewWidgetMarkup markup
+ = variableMemoryMarkup(m, address, size, sizeIsEstimate,
+ w->textCharFormat(), background);
+ w->init(address, qMax(size, LocalsMemoryViewWidget::defaultLength), nameOf(m));
+ w->setMarkup(markup);
+ w->move(p);
+ engine->addMemoryView(w);
+}
+
/////////////////////////////////////////////////////////////////////
//
// WatchWindow
: QTreeView(parent),
m_type(type)
{
+ setObjectName(QLatin1String("WatchWindow"));
m_grabbing = false;
setFrameStyle(QFrame::NoFrame);
const QModelIndex mi0 = idx.sibling(idx.row(), 0);
const QModelIndex mi1 = idx.sibling(idx.row(), 1);
const QModelIndex mi2 = idx.sibling(idx.row(), 2);
- const quint64 address = mi0.data(LocalsAddressRole).toULongLong();
- const uint size = mi0.data(LocalsSizeRole).toUInt();
- const quint64 pointerValue = mi0.data(LocalsPointerValueRole).toULongLong();
+ const quint64 address = addressOf(mi0);
+ const uint size = sizeOf(mi0);
+ const quint64 pointerValue = pointerValueOf(mi0);
const QString exp = mi0.data(LocalsExpressionRole).toString();
const QString type = mi2.data().toString();
QAction *actOpenMemoryEditAtVariableAddress = new QAction(&memoryMenu);
QAction *actOpenMemoryEditAtPointerValue = new QAction(&memoryMenu);
QAction *actOpenMemoryEditor = new QAction(&memoryMenu);
+ QAction *actOpenMemoryViewAtVariableAddress = new QAction(&memoryMenu);
+ QAction *actOpenMemoryViewAtPointerValue = new QAction(&memoryMenu);
if (engineCapabilities & ShowMemoryCapability) {
actOpenMemoryEditor->setText(tr("Open Memory Editor..."));
if (address) {
actOpenMemoryEditAtVariableAddress->setText(
tr("Open Memory Editor at Object's Address (0x%1)")
.arg(address, 0, 16));
+ actOpenMemoryViewAtVariableAddress->setText(
+ tr("Open Memory View at Object's Address (0x%1)")
+ .arg(address, 0, 16));
} else {
actOpenMemoryEditAtVariableAddress->setText(
tr("Open Memory Editor at Object's Address"));
actOpenMemoryEditAtVariableAddress->setEnabled(false);
+ actOpenMemoryViewAtVariableAddress->setText(
+ tr("Open Memory View at Object's Address"));
+ actOpenMemoryViewAtVariableAddress->setEnabled(false);
}
if (createPointerActions) {
actOpenMemoryEditAtPointerValue->setText(
tr("Open Memory Editor at Referenced Address (0x%1)")
.arg(pointerValue, 0, 16));
+ actOpenMemoryViewAtPointerValue->setText(
+ tr("Open Memory View at Referenced Address (0x%1)")
+ .arg(pointerValue, 0, 16));
} else {
actOpenMemoryEditAtPointerValue->setText(
tr("Open Memory Editor at Referenced Address"));
actOpenMemoryEditAtPointerValue->setEnabled(false);
+ actOpenMemoryViewAtPointerValue->setText(
+ tr("Open Memory View at Referenced Address"));
+ actOpenMemoryViewAtPointerValue->setEnabled(false);
}
+ memoryMenu.addAction(actOpenMemoryViewAtVariableAddress);
+ memoryMenu.addAction(actOpenMemoryViewAtPointerValue);
memoryMenu.addAction(actOpenMemoryEditAtVariableAddress);
memoryMenu.addAction(actOpenMemoryEditAtPointerValue);
memoryMenu.addAction(actOpenMemoryEditor);
AddressDialog dialog;
if (dialog.exec() == QDialog::Accepted)
currentEngine()->openMemoryView(dialog.address());
+ } else if (act == actOpenMemoryViewAtVariableAddress) {
+ addVariableMemoryView(currentEngine(), mi0, false, ev->globalPos(), this);
+ } else if (act == actOpenMemoryViewAtPointerValue) {
+ addVariableMemoryView(currentEngine(), mi0, true, ev->globalPos(), this);
} else if (act == actSetWatchpointAtVariableAddress) {
setWatchpoint(address, size);
} else if (act == actSetWatchpointAtPointerValue) {