From 9af9c91f93fb118c4c54ed46c9bc58ce708012b5 Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Wed, 19 May 2010 17:34:47 +0200
Subject: [PATCH] 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.
---
 src/plugins/debugger/breakhandler.cpp | 10 +++
 src/plugins/debugger/breakhandler.h   |  1 +
 src/plugins/debugger/watchhandler.cpp | 41 +++++++++---
 src/plugins/debugger/watchhandler.h   |  4 +-
 src/plugins/debugger/watchwindow.cpp  | 95 ++++++++++++++++++---------
 5 files changed, 110 insertions(+), 41 deletions(-)

diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp
index d9a20d08a80..e194fbdf462 100644
--- a/src/plugins/debugger/breakhandler.cpp
+++ b/src/plugins/debugger/breakhandler.cpp
@@ -406,6 +406,16 @@ BreakpointData *BreakHandler::findBreakpointByNumber(int bpNumber) const
     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()
 {
     QList<QVariant> list;
diff --git a/src/plugins/debugger/breakhandler.h b/src/plugins/debugger/breakhandler.h
index 03d3f557d69..77ed135d177 100644
--- a/src/plugins/debugger/breakhandler.h
+++ b/src/plugins/debugger/breakhandler.h
@@ -70,6 +70,7 @@ public:
     // Find a breakpoint matching approximately the data in needle.bp*,
     BreakpointData *findSimilarBreakpoint(const BreakpointData &needle) const;
     BreakpointData *findBreakpointByNumber(int bpNumber) const;
+    BreakpointData *findWatchPointByAddress(const QByteArray &a) const;
     void updateMarkers();
 
     QList<BreakpointData *> insertedBreakpoints() const;
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
index 3be39eea5ac..c74d4ef777f 100644
--- a/src/plugins/debugger/watchhandler.cpp
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -564,6 +564,23 @@ static inline QString truncateValue(QString 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
 {
     const WatchItem *item = watchItem(idx);
@@ -636,16 +653,22 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
         case IndividualFormatRole:
             return m_handler->m_individualFormats.value(data.addr, -1);
 
-        case AddressRole: {
-            if (!data.addr.isEmpty())
-                return data.addr;
-            bool ok;
-            (void) data.value.toULongLong(&ok, 0);
-            if (ok)
-                return data.value;
-            return QVariant();
-        }
+        case AddressRole:
+            if (!data.addr.isEmpty()) {
+                bool ok;
+                const quint64 address = data.addr.toULongLong(&ok, 16);
+                if (ok)
+                    return QVariant(address);
+            }
+            return QVariant(quint64(0));
+
+        case RawValueRole:
+            return data.value;
 
+        case PointerValue:
+            if (isPointerType(data.type))
+                return pointerValue(data.value);
+            return QVariant(quint64(0));
         default:
             break;
     }
diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h
index 9a4dad79a80..daf8507e67e 100644
--- a/src/plugins/debugger/watchhandler.h
+++ b/src/plugins/debugger/watchhandler.h
@@ -61,7 +61,9 @@ enum WatchRoles
     TypeFormatListRole,
     TypeFormatRole,  // Used to communicate alternative formats to the view.
     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
diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp
index 209944278a0..b3782448a2e 100644
--- a/src/plugins/debugger/watchwindow.cpp
+++ b/src/plugins/debugger/watchwindow.cpp
@@ -204,13 +204,26 @@ void WatchWindow::dropEvent(QDropEvent *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)
 {
     const QModelIndex idx = indexAt(ev->pos());
     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 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 type = model()->data(mi2).toString();
 
@@ -255,9 +268,9 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
     QMenu individualFormatMenu;
     QList<QAction *> individualFormatActions;
     QAction *clearIndividualFormatAction = 0;
-    if (idx.isValid() && !addr.isEmpty()) {
+    if (idx.isValid() && address) {
         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()) {
             individualFormatMenu.setEnabled(false);
         } else {
@@ -292,27 +305,39 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
     QAction *actSelectWidgetToWatch = menu.addAction(tr("Select Widget to Watch"));
     actSelectWidgetToWatch->setEnabled(canHandleWatches);
 
-    const QString address = model()->data(mi0, AddressRole).toString();
-    QAction *actWatchKnownMemory = 0;
-    QAction *actWatchUnknownMemory =
+    QAction *actOpenMemoryEditAtVariableAddress = 0;
+    QAction *actOpenMemoryEditAtPointerValue = 0;
+    QAction *actOpenMemoryEditor =
         new QAction(tr("Open Memory Editor..."), &menu);
     const bool canShowMemory = engineCapabilities & ShowMemoryCapability;
-    actWatchUnknownMemory->setEnabled(actionsEnabled && canShowMemory);
-
-    if (canShowMemory && !address.isEmpty())
-        actWatchKnownMemory =
-            new QAction(tr("Open Memory Editor at %1").arg(address), &menu);
+    actOpenMemoryEditor->setEnabled(actionsEnabled && canShowMemory);
+
+    // Offer to open address pointed to or variable address.
+    const bool createPointerActions = pointerValue &&  pointerValue != address;
+
+    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();
 
-    QAction *actSetWatchpoint = 0;
+    QAction *actSetWatchPointAtVariableAddress = 0;
+    QAction *actSetWatchPointAtPointerValue= 0;
     const bool canSetWatchpoint = engineCapabilities & WatchpointCapability;
-    if (canSetWatchpoint && !address.isEmpty()) {
-        actSetWatchpoint =
-            new QAction(tr("Break on Changing Contents of %1").arg(address), &menu);
+    if (canSetWatchpoint && address) {
+        const QString watchPointFormat = tr("Break on Changing Contents of 0x%1");
+        actSetWatchPointAtVariableAddress =
+            new QAction(watchPointFormat.arg(address, 0, 16), &menu);
+        if (createPointerActions)
+            actSetWatchPointAtPointerValue =
+                    new QAction(watchPointFormat.arg(pointerValue, 0, 16), &menu);
     } else {
-        actSetWatchpoint =
+        actSetWatchPointAtVariableAddress =
             new QAction(tr("Break on Changing Contents"), &menu);
-        actSetWatchpoint->setEnabled(false);
+        actSetWatchPointAtVariableAddress->setEnabled(false);
     }
 
     QAction *actWatchOrRemove;
@@ -330,10 +355,14 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
     menu.addAction(actSelectWidgetToWatch);
     menu.addMenu(&typeFormatMenu);
     menu.addMenu(&individualFormatMenu);
-    if (actWatchKnownMemory)
-        menu.addAction(actWatchKnownMemory);
-    menu.addAction(actWatchUnknownMemory);
-    menu.addAction(actSetWatchpoint);
+    if (actOpenMemoryEditAtVariableAddress)
+        menu.addAction(actOpenMemoryEditAtVariableAddress);
+    if (actOpenMemoryEditAtPointerValue)
+        menu.addAction(actOpenMemoryEditAtPointerValue);
+    menu.addAction(actOpenMemoryEditor);
+    menu.addAction(actSetWatchPointAtVariableAddress);
+    if (actSetWatchPointAtPointerValue)
+        menu.addAction(actSetWatchPointAtPointerValue);
     menu.addSeparator();
 
     menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
@@ -361,6 +390,8 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
     menu.addAction(theDebuggerAction(SettingsDialog));
 
     QAction *act = menu.exec(ev->globalPos());
+    if (act == 0)
+        return;
 
     if (act == actAdjustColumnWidths) {
         resizeColumnsToContents();
@@ -369,27 +400,29 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
     } else if (act == actInsertNewWatchItem) {
         theDebuggerAction(WatchExpression)
             ->trigger(WatchHandler::watcherEditPlaceHolder());
-    } else if (actWatchKnownMemory != 0 && act == actWatchKnownMemory) {
+    } else if (act == actOpenMemoryEditAtVariableAddress) {
         (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;
         if (dialog.exec() == QDialog::Accepted) {
             (void) new MemoryViewAgent(m_manager, dialog.address());
         }
-    } else if (act == actSetWatchpoint) {
-        BreakpointData *data = new BreakpointData;
-        data->type = BreakpointData::WatchpointType;
-        data->address = address.toLatin1();
-        m_manager->breakHandler()->appendBreakpoint(data);
-        m_manager->attemptBreakpointSynchronization();
+    } else if (act == actSetWatchPointAtVariableAddress) {
+        addWatchPoint(m_manager, address);
+    } else if (act == actSetWatchPointAtVariableAddress) {
+        addWatchPoint(m_manager, address);
+    } else if (act == actSetWatchPointAtPointerValue) {
+        addWatchPoint(m_manager, pointerValue);
     } else if (act == actSelectWidgetToWatch) {
         grabMouse(Qt::CrossCursor);
         m_grabbing = true;
     } else if (act == actClearCodeModelSnapshot) {
         m_manager->clearCppCodeModelSnapshot();
-    } else if (clearTypeFormatAction && act == clearTypeFormatAction) {
+    } else if (act == clearTypeFormatAction) {
         model()->setData(mi1, -1, TypeFormatRole);
-    } else if (clearIndividualFormatAction && act == clearIndividualFormatAction) {
+    } else if (act == clearIndividualFormatAction) {
         model()->setData(mi1, -1, IndividualFormatRole);
     } else {
         for (int i = 0; i != typeFormatActions.size(); ++i) {
-- 
GitLab