forked from qt-creator/qt-creator
Debugger/WatchWindow: Add menu options to show pointed to addresses.
Change the WatchWindow handling to be based on quint64 addresses to ensure proper function. Changed roles and added one role to the watchmodel to obtain addresses and pointed-to addresses. Add some fiddling to parse out addresses from debugger values. Add menu options. Make sure only one watchpoint per address is added.
This commit is contained in:
@@ -406,6 +406,16 @@ BreakpointData *BreakHandler::findBreakpointByNumber(int bpNumber) const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BreakpointData *BreakHandler::findWatchPointByAddress(const QByteArray &a) const
|
||||||
|
{
|
||||||
|
for (int index = size() - 1; index >= 0; --index) {
|
||||||
|
BreakpointData *bd = at(index);
|
||||||
|
if (bd->type == BreakpointData::WatchpointType && bd->address == a)
|
||||||
|
return bd;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void BreakHandler::saveBreakpoints()
|
void BreakHandler::saveBreakpoints()
|
||||||
{
|
{
|
||||||
QList<QVariant> list;
|
QList<QVariant> list;
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ public:
|
|||||||
// Find a breakpoint matching approximately the data in needle.bp*,
|
// Find a breakpoint matching approximately the data in needle.bp*,
|
||||||
BreakpointData *findSimilarBreakpoint(const BreakpointData &needle) const;
|
BreakpointData *findSimilarBreakpoint(const BreakpointData &needle) const;
|
||||||
BreakpointData *findBreakpointByNumber(int bpNumber) const;
|
BreakpointData *findBreakpointByNumber(int bpNumber) const;
|
||||||
|
BreakpointData *findWatchPointByAddress(const QByteArray &a) const;
|
||||||
void updateMarkers();
|
void updateMarkers();
|
||||||
|
|
||||||
QList<BreakpointData *> insertedBreakpoints() const;
|
QList<BreakpointData *> insertedBreakpoints() const;
|
||||||
|
|||||||
@@ -564,6 +564,23 @@ static inline QString truncateValue(QString v)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get a pointer address from pointer values reported by the debugger.
|
||||||
|
// Fix CDB formatting of pointers "0x00000000`000003fd class foo *",
|
||||||
|
// check gdb formatting of characters.
|
||||||
|
static inline quint64 pointerValue(QString data)
|
||||||
|
{
|
||||||
|
if (data.isEmpty() || !data.startsWith(QLatin1String("0x")))
|
||||||
|
return 0;
|
||||||
|
data.remove(0, 2);
|
||||||
|
const int blankPos = data.indexOf(QLatin1Char(' '));
|
||||||
|
if (blankPos != -1)
|
||||||
|
data.truncate(blankPos);
|
||||||
|
data.remove(QLatin1Char('`'));
|
||||||
|
bool ok;
|
||||||
|
const quint64 address = data.toULongLong(&ok, 16);
|
||||||
|
return ok ? address : quint64(0);
|
||||||
|
}
|
||||||
|
|
||||||
QVariant WatchModel::data(const QModelIndex &idx, int role) const
|
QVariant WatchModel::data(const QModelIndex &idx, int role) const
|
||||||
{
|
{
|
||||||
const WatchItem *item = watchItem(idx);
|
const WatchItem *item = watchItem(idx);
|
||||||
@@ -636,16 +653,22 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
|
|||||||
case IndividualFormatRole:
|
case IndividualFormatRole:
|
||||||
return m_handler->m_individualFormats.value(data.addr, -1);
|
return m_handler->m_individualFormats.value(data.addr, -1);
|
||||||
|
|
||||||
case AddressRole: {
|
case AddressRole:
|
||||||
if (!data.addr.isEmpty())
|
if (!data.addr.isEmpty()) {
|
||||||
return data.addr;
|
bool ok;
|
||||||
bool ok;
|
const quint64 address = data.addr.toULongLong(&ok, 16);
|
||||||
(void) data.value.toULongLong(&ok, 0);
|
if (ok)
|
||||||
if (ok)
|
return QVariant(address);
|
||||||
return data.value;
|
}
|
||||||
return QVariant();
|
return QVariant(quint64(0));
|
||||||
}
|
|
||||||
|
|
||||||
|
case RawValueRole:
|
||||||
|
return data.value;
|
||||||
|
|
||||||
|
case PointerValue:
|
||||||
|
if (isPointerType(data.type))
|
||||||
|
return pointerValue(data.value);
|
||||||
|
return QVariant(quint64(0));
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,9 @@ enum WatchRoles
|
|||||||
TypeFormatListRole,
|
TypeFormatListRole,
|
||||||
TypeFormatRole, // Used to communicate alternative formats to the view.
|
TypeFormatRole, // Used to communicate alternative formats to the view.
|
||||||
IndividualFormatRole,
|
IndividualFormatRole,
|
||||||
AddressRole, // Some memory address related to the object.
|
AddressRole, // Memory address of variable as quint64.
|
||||||
|
RawValueRole, // Unformatted value as string.
|
||||||
|
PointerValue // Pointer value (address) as quint64.
|
||||||
};
|
};
|
||||||
|
|
||||||
enum IntegerFormat
|
enum IntegerFormat
|
||||||
|
|||||||
@@ -204,13 +204,26 @@ void WatchWindow::dropEvent(QDropEvent *ev)
|
|||||||
//QTreeView::dropEvent(ev);
|
//QTreeView::dropEvent(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void addWatchPoint(DebuggerManager *manager, quint64 address)
|
||||||
|
{
|
||||||
|
const QByteArray addressBA = QByteArray("0x") + QByteArray::number(address, 16);
|
||||||
|
if (manager->breakHandler()->findWatchPointByAddress(addressBA))
|
||||||
|
return;
|
||||||
|
BreakpointData *data = new BreakpointData;
|
||||||
|
data->type = BreakpointData::WatchpointType;
|
||||||
|
data->address = addressBA;
|
||||||
|
manager->breakHandler()->appendBreakpoint(data);
|
||||||
|
manager->attemptBreakpointSynchronization();
|
||||||
|
}
|
||||||
|
|
||||||
void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
|
void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
|
||||||
{
|
{
|
||||||
const QModelIndex idx = indexAt(ev->pos());
|
const QModelIndex idx = indexAt(ev->pos());
|
||||||
const QModelIndex mi0 = idx.sibling(idx.row(), 0);
|
const QModelIndex mi0 = idx.sibling(idx.row(), 0);
|
||||||
const QModelIndex mi1 = idx.sibling(idx.row(), 1);
|
const QModelIndex mi1 = idx.sibling(idx.row(), 1);
|
||||||
const QModelIndex mi2 = idx.sibling(idx.row(), 2);
|
const QModelIndex mi2 = idx.sibling(idx.row(), 2);
|
||||||
const QString addr = model()->data(mi0, AddressRole).toString();
|
const quint64 address = model()->data(mi0, AddressRole).toULongLong();
|
||||||
|
const quint64 pointerValue = model()->data(mi0, PointerValue).toULongLong();
|
||||||
const QString exp = model()->data(mi0, ExpressionRole).toString();
|
const QString exp = model()->data(mi0, ExpressionRole).toString();
|
||||||
const QString type = model()->data(mi2).toString();
|
const QString type = model()->data(mi2).toString();
|
||||||
|
|
||||||
@@ -255,9 +268,9 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
|
|||||||
QMenu individualFormatMenu;
|
QMenu individualFormatMenu;
|
||||||
QList<QAction *> individualFormatActions;
|
QList<QAction *> individualFormatActions;
|
||||||
QAction *clearIndividualFormatAction = 0;
|
QAction *clearIndividualFormatAction = 0;
|
||||||
if (idx.isValid() && !addr.isEmpty()) {
|
if (idx.isValid() && address) {
|
||||||
individualFormatMenu.setTitle(
|
individualFormatMenu.setTitle(
|
||||||
tr("Change Format for Object at %1").arg(addr));
|
tr("Change Format for Object at 0x%1").arg(address, 0, 16));
|
||||||
if (alternativeFormats.isEmpty()) {
|
if (alternativeFormats.isEmpty()) {
|
||||||
individualFormatMenu.setEnabled(false);
|
individualFormatMenu.setEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
@@ -292,27 +305,39 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
|
|||||||
QAction *actSelectWidgetToWatch = menu.addAction(tr("Select Widget to Watch"));
|
QAction *actSelectWidgetToWatch = menu.addAction(tr("Select Widget to Watch"));
|
||||||
actSelectWidgetToWatch->setEnabled(canHandleWatches);
|
actSelectWidgetToWatch->setEnabled(canHandleWatches);
|
||||||
|
|
||||||
const QString address = model()->data(mi0, AddressRole).toString();
|
QAction *actOpenMemoryEditAtVariableAddress = 0;
|
||||||
QAction *actWatchKnownMemory = 0;
|
QAction *actOpenMemoryEditAtPointerValue = 0;
|
||||||
QAction *actWatchUnknownMemory =
|
QAction *actOpenMemoryEditor =
|
||||||
new QAction(tr("Open Memory Editor..."), &menu);
|
new QAction(tr("Open Memory Editor..."), &menu);
|
||||||
const bool canShowMemory = engineCapabilities & ShowMemoryCapability;
|
const bool canShowMemory = engineCapabilities & ShowMemoryCapability;
|
||||||
actWatchUnknownMemory->setEnabled(actionsEnabled && canShowMemory);
|
actOpenMemoryEditor->setEnabled(actionsEnabled && canShowMemory);
|
||||||
|
|
||||||
if (canShowMemory && !address.isEmpty())
|
// Offer to open address pointed to or variable address.
|
||||||
actWatchKnownMemory =
|
const bool createPointerActions = pointerValue && pointerValue != address;
|
||||||
new QAction(tr("Open Memory Editor at %1").arg(address), &menu);
|
|
||||||
|
const QString openMemoryEditorFormat = tr("Open Memory Editor at 0x%1");
|
||||||
|
if (canShowMemory && address)
|
||||||
|
actOpenMemoryEditAtVariableAddress =
|
||||||
|
new QAction(openMemoryEditorFormat.arg(address, 0, 16), &menu);
|
||||||
|
if (createPointerActions)
|
||||||
|
actOpenMemoryEditAtPointerValue =
|
||||||
|
new QAction(openMemoryEditorFormat.arg(pointerValue, 0, 16), &menu);
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
|
|
||||||
QAction *actSetWatchpoint = 0;
|
QAction *actSetWatchPointAtVariableAddress = 0;
|
||||||
|
QAction *actSetWatchPointAtPointerValue= 0;
|
||||||
const bool canSetWatchpoint = engineCapabilities & WatchpointCapability;
|
const bool canSetWatchpoint = engineCapabilities & WatchpointCapability;
|
||||||
if (canSetWatchpoint && !address.isEmpty()) {
|
if (canSetWatchpoint && address) {
|
||||||
actSetWatchpoint =
|
const QString watchPointFormat = tr("Break on Changing Contents of 0x%1");
|
||||||
new QAction(tr("Break on Changing Contents of %1").arg(address), &menu);
|
actSetWatchPointAtVariableAddress =
|
||||||
|
new QAction(watchPointFormat.arg(address, 0, 16), &menu);
|
||||||
|
if (createPointerActions)
|
||||||
|
actSetWatchPointAtPointerValue =
|
||||||
|
new QAction(watchPointFormat.arg(pointerValue, 0, 16), &menu);
|
||||||
} else {
|
} else {
|
||||||
actSetWatchpoint =
|
actSetWatchPointAtVariableAddress =
|
||||||
new QAction(tr("Break on Changing Contents"), &menu);
|
new QAction(tr("Break on Changing Contents"), &menu);
|
||||||
actSetWatchpoint->setEnabled(false);
|
actSetWatchPointAtVariableAddress->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
QAction *actWatchOrRemove;
|
QAction *actWatchOrRemove;
|
||||||
@@ -330,10 +355,14 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
|
|||||||
menu.addAction(actSelectWidgetToWatch);
|
menu.addAction(actSelectWidgetToWatch);
|
||||||
menu.addMenu(&typeFormatMenu);
|
menu.addMenu(&typeFormatMenu);
|
||||||
menu.addMenu(&individualFormatMenu);
|
menu.addMenu(&individualFormatMenu);
|
||||||
if (actWatchKnownMemory)
|
if (actOpenMemoryEditAtVariableAddress)
|
||||||
menu.addAction(actWatchKnownMemory);
|
menu.addAction(actOpenMemoryEditAtVariableAddress);
|
||||||
menu.addAction(actWatchUnknownMemory);
|
if (actOpenMemoryEditAtPointerValue)
|
||||||
menu.addAction(actSetWatchpoint);
|
menu.addAction(actOpenMemoryEditAtPointerValue);
|
||||||
|
menu.addAction(actOpenMemoryEditor);
|
||||||
|
menu.addAction(actSetWatchPointAtVariableAddress);
|
||||||
|
if (actSetWatchPointAtPointerValue)
|
||||||
|
menu.addAction(actSetWatchPointAtPointerValue);
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
|
|
||||||
menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
|
menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
|
||||||
@@ -361,6 +390,8 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
|
|||||||
menu.addAction(theDebuggerAction(SettingsDialog));
|
menu.addAction(theDebuggerAction(SettingsDialog));
|
||||||
|
|
||||||
QAction *act = menu.exec(ev->globalPos());
|
QAction *act = menu.exec(ev->globalPos());
|
||||||
|
if (act == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (act == actAdjustColumnWidths) {
|
if (act == actAdjustColumnWidths) {
|
||||||
resizeColumnsToContents();
|
resizeColumnsToContents();
|
||||||
@@ -369,27 +400,29 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
|
|||||||
} else if (act == actInsertNewWatchItem) {
|
} else if (act == actInsertNewWatchItem) {
|
||||||
theDebuggerAction(WatchExpression)
|
theDebuggerAction(WatchExpression)
|
||||||
->trigger(WatchHandler::watcherEditPlaceHolder());
|
->trigger(WatchHandler::watcherEditPlaceHolder());
|
||||||
} else if (actWatchKnownMemory != 0 && act == actWatchKnownMemory) {
|
} else if (act == actOpenMemoryEditAtVariableAddress) {
|
||||||
(void) new MemoryViewAgent(m_manager, address);
|
(void) new MemoryViewAgent(m_manager, address);
|
||||||
} else if (actWatchUnknownMemory != 0 && act == actWatchUnknownMemory) {
|
} else if (act == actOpenMemoryEditAtPointerValue) {
|
||||||
|
(void) new MemoryViewAgent(m_manager, pointerValue);
|
||||||
|
} else if (act == actOpenMemoryEditor) {
|
||||||
AddressDialog dialog;
|
AddressDialog dialog;
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
(void) new MemoryViewAgent(m_manager, dialog.address());
|
(void) new MemoryViewAgent(m_manager, dialog.address());
|
||||||
}
|
}
|
||||||
} else if (act == actSetWatchpoint) {
|
} else if (act == actSetWatchPointAtVariableAddress) {
|
||||||
BreakpointData *data = new BreakpointData;
|
addWatchPoint(m_manager, address);
|
||||||
data->type = BreakpointData::WatchpointType;
|
} else if (act == actSetWatchPointAtVariableAddress) {
|
||||||
data->address = address.toLatin1();
|
addWatchPoint(m_manager, address);
|
||||||
m_manager->breakHandler()->appendBreakpoint(data);
|
} else if (act == actSetWatchPointAtPointerValue) {
|
||||||
m_manager->attemptBreakpointSynchronization();
|
addWatchPoint(m_manager, pointerValue);
|
||||||
} else if (act == actSelectWidgetToWatch) {
|
} else if (act == actSelectWidgetToWatch) {
|
||||||
grabMouse(Qt::CrossCursor);
|
grabMouse(Qt::CrossCursor);
|
||||||
m_grabbing = true;
|
m_grabbing = true;
|
||||||
} else if (act == actClearCodeModelSnapshot) {
|
} else if (act == actClearCodeModelSnapshot) {
|
||||||
m_manager->clearCppCodeModelSnapshot();
|
m_manager->clearCppCodeModelSnapshot();
|
||||||
} else if (clearTypeFormatAction && act == clearTypeFormatAction) {
|
} else if (act == clearTypeFormatAction) {
|
||||||
model()->setData(mi1, -1, TypeFormatRole);
|
model()->setData(mi1, -1, TypeFormatRole);
|
||||||
} else if (clearIndividualFormatAction && act == clearIndividualFormatAction) {
|
} else if (act == clearIndividualFormatAction) {
|
||||||
model()->setData(mi1, -1, IndividualFormatRole);
|
model()->setData(mi1, -1, IndividualFormatRole);
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i != typeFormatActions.size(); ++i) {
|
for (int i = 0; i != typeFormatActions.size(); ++i) {
|
||||||
|
|||||||
Reference in New Issue
Block a user