From 89ffffc183f2fd2fe2070bd25620a9758371ed34 Mon Sep 17 00:00:00 2001
From: hjk <qtc-committer@nokia.com>
Date: Wed, 12 Aug 2009 10:51:25 +0200
Subject: [PATCH] debugger: use the bineditor to show memory dumps

---
 dist/changes-1.3.0                       |  1 +
 src/plugins/bineditor/bineditor.cpp      |  9 ++-
 src/plugins/bineditor/bineditor.h        | 17 ++---
 src/plugins/bineditor/bineditorplugin.h  |  2 +-
 src/plugins/debugger/debugger.pro        |  8 ++-
 src/plugins/debugger/debuggeractions.h   | 13 ++--
 src/plugins/debugger/debuggeragents.cpp  | 73 ++++++++++++++++++++
 src/plugins/debugger/debuggeragents.h    | 80 ++++++++++++++++++++++
 src/plugins/debugger/debuggerdialogs.h   |  1 +
 src/plugins/debugger/debuggermanager.cpp |  7 +-
 src/plugins/debugger/debuggermanager.h   | 11 ++--
 src/plugins/debugger/debuggerplugin.cpp  |  4 ++
 src/plugins/debugger/gdb/gdbengine.cpp   | 35 ++++++++++
 src/plugins/debugger/gdb/gdbengine.h     |  3 +
 src/plugins/debugger/idebuggerengine.h   |  3 +
 src/plugins/debugger/watchhandler.cpp    | 10 +++
 src/plugins/debugger/watchhandler.h      |  3 +-
 src/plugins/debugger/watchwindow.cpp     | 84 +++++++++++++++++-------
 src/plugins/debugger/watchwindow.h       |  5 +-
 19 files changed, 312 insertions(+), 57 deletions(-)
 create mode 100644 src/plugins/debugger/debuggeragents.cpp
 create mode 100644 src/plugins/debugger/debuggeragents.h

diff --git a/dist/changes-1.3.0 b/dist/changes-1.3.0
index 4206ab0f400..dbdca175f2a 100644
--- a/dist/changes-1.3.0
+++ b/dist/changes-1.3.0
@@ -39,6 +39,7 @@ Debugging
    * CDB: Fixed thread handling
    * CDB: Added internal dumpers for string types for debuggee crashes
    * Improved QObject dumping, print out QRect/QSize, enumerations and flags
+   * Use the BinEditor plugin to show raw memory
 
 Designer
    * Added support for rearranging and floating form editor tools
diff --git a/src/plugins/bineditor/bineditor.cpp b/src/plugins/bineditor/bineditor.cpp
index 69da9ef1329..d84bc3f3d29 100644
--- a/src/plugins/bineditor/bineditor.cpp
+++ b/src/plugins/bineditor/bineditor.cpp
@@ -32,14 +32,13 @@
 #include <texteditor/fontsettings.h>
 #include <texteditor/texteditorconstants.h>
 
-#include <QtGui/QScrollBar>
+#include <QtCore/QByteArrayMatcher>
+#include <QtGui/QApplication>
+#include <QtGui/QClipboard>
 #include <QtGui/QFontMetrics>
 #include <QtGui/QPainter>
 #include <QtGui/QScrollBar>
 #include <QtGui/QWheelEvent>
-#include <QtGui/QApplication>
-#include <QtGui/QClipboard>
-#include <QtCore/QByteArrayMatcher>
 
 using namespace BINEditor;
 
@@ -794,7 +793,7 @@ void BinEditor::paintEvent(QPaintEvent *e)
             }
         }
 
-        if (cursor >= 0) {
+        if (cursor >= 0 && !printable.isEmpty()) {
             QRect cursorRect(text_x + painter.fontMetrics().width(printable.left(cursor)),
                              y-m_ascent,
                              painter.fontMetrics().width(printable.at(cursor)),
diff --git a/src/plugins/bineditor/bineditor.h b/src/plugins/bineditor/bineditor.h
index 3349ad3e04c..86b0e9d1ca9 100644
--- a/src/plugins/bineditor/bineditor.h
+++ b/src/plugins/bineditor/bineditor.h
@@ -30,12 +30,13 @@
 #ifndef BINEDITOR_H
 #define BINEDITOR_H
 
-#include <QtGui/qabstractscrollarea.h>
-#include <QtCore/qbasictimer.h>
-#include <QtCore/qstack.h>
-#include <QtCore/qset.h>
-#include <QtGui/qtextdocument.h>
-#include <QtGui/qtextformat.h>
+#include <QtCore/QBasicTimer>
+#include <QtCore/QSet>
+#include <QtCore/QStack>
+
+#include <QtGui/QAbstractScrollArea>
+#include <QtGui/QTextDocument>
+#include <QtGui/QTextFormat>
 
 namespace Core {
 class IEditor;
@@ -63,9 +64,9 @@ public:
     inline int dataSize() const { return m_size; }
 
     inline bool inLazyMode() const { return m_inLazyMode; }
-    void setLazyData(int cursorPosition, int size, int blockSize = 4096);
+    Q_INVOKABLE void setLazyData(int cursorPosition, int size, int blockSize = 4096);
     inline int lazyDataBlockSize() const { return m_blockSize; }
-    void addLazyData(int block, const QByteArray &data);
+    Q_INVOKABLE void addLazyData(int block, const QByteArray &data);
     bool applyModifications(QByteArray &data) const;
 
     void zoomIn(int range = 1);
diff --git a/src/plugins/bineditor/bineditorplugin.h b/src/plugins/bineditor/bineditorplugin.h
index 1935f65834a..09424606aed 100644
--- a/src/plugins/bineditor/bineditorplugin.h
+++ b/src/plugins/bineditor/bineditorplugin.h
@@ -33,7 +33,7 @@
 #include <extensionsystem/iplugin.h>
 #include <coreplugin/editormanager/ieditorfactory.h>
 
-#include <QtCore/qplugin.h>
+#include <QtCore/QtPlugin>
 #include <QtCore/QPointer>
 #include <QtCore/QStringList>
 #include <QtGui/QAction>
diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro
index 25b7c355f07..f6b56bb66dd 100644
--- a/src/plugins/debugger/debugger.pro
+++ b/src/plugins/debugger/debugger.pro
@@ -6,11 +6,11 @@ TARGET = Debugger
 
 # CONFIG += single
 include(../../qtcreatorplugin.pri)
-include(../../plugins/projectexplorer/projectexplorer.pri)
-include(../../plugins/find/find.pri)
 include(../../plugins/coreplugin/coreplugin.pri)
-include(../../plugins/texteditor/texteditor.pri)
 include(../../plugins/cpptools/cpptools.pri)
+include(../../plugins/find/find.pri)
+include(../../plugins/projectexplorer/projectexplorer.pri)
+include(../../plugins/texteditor/texteditor.pri)
 include(../../libs/cplusplus/cplusplus.pri)
 include(../../libs/utils/utils.pri)
 INCLUDEPATH += $$PWD/../../libs/utils
@@ -20,6 +20,7 @@ QT += gui network script
 HEADERS += \
     breakhandler.h \
     breakwindow.h \
+    debuggeragents.h \
     debuggeractions.h \
     debuggerconstants.h \
     debuggerdialogs.h \
@@ -50,6 +51,7 @@ SOURCES += \
     breakhandler.cpp \
     breakwindow.cpp \
     breakwindow.h \
+    debuggeragents.cpp \
     debuggeractions.cpp \
     debuggerdialogs.cpp \
     debuggermanager.cpp \
diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h
index 5f953b6d10e..a0ccf46c005 100644
--- a/src/plugins/debugger/debuggeractions.h
+++ b/src/plugins/debugger/debuggeractions.h
@@ -37,6 +37,7 @@
 QT_BEGIN_NAMESPACE
 class QActionGroup;
 QT_END_NAMESPACE
+
 namespace Debugger {
 namespace Internal {
 
@@ -80,6 +81,12 @@ enum DebuggerActionCode
     LockView,
     LogTimeStamps,
 
+    RecheckDebuggingHelpers,
+    UseDebuggingHelpers,
+    UseCustomDebuggingHelperLocation,
+    CustomDebuggingHelperLocation,
+    DebugDebuggingHelpers,
+    
     // Gdb
     GdbLocation,
     GdbEnvironment,
@@ -99,12 +106,6 @@ enum DebuggerActionCode
     AssignValue,
     AssignType,
 
-    RecheckDebuggingHelpers,
-    UseDebuggingHelpers,
-    UseCustomDebuggingHelperLocation,
-    CustomDebuggingHelperLocation,
-    DebugDebuggingHelpers,
-
     // Source List
     ListSourceFiles,
 
diff --git a/src/plugins/debugger/debuggeragents.cpp b/src/plugins/debugger/debuggeragents.cpp
new file mode 100644
index 00000000000..e221ee15277
--- /dev/null
+++ b/src/plugins/debugger/debuggeragents.cpp
@@ -0,0 +1,73 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#include "debuggeragents.h"
+#include "idebuggerengine.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+
+namespace Debugger {
+namespace Internal {
+
+MemoryViewAgent::MemoryViewAgent(DebuggerManager *manager, quint64 addr)
+    : QObject(manager), m_engine(manager->currentEngine())
+{
+    Core::EditorManager *editorManager = Core::EditorManager::instance();
+    QString titlePattern = "Memory $";
+    m_editor = editorManager->openEditorWithContents(
+        Core::Constants::K_DEFAULT_BINARY_EDITOR,
+        &titlePattern);
+    connect(m_editor->widget(), SIGNAL(lazyDataRequested(int,bool)),
+        this, SLOT(fetchLazyData(int,bool)));
+    editorManager->activateEditor(m_editor);
+    QMetaObject::invokeMethod(m_editor->widget(), "setLazyData",
+    Q_ARG(int, addr), Q_ARG(int, INT_MAX), Q_ARG(int, BinBlockSize));
+}
+
+MemoryViewAgent::~MemoryViewAgent()
+{
+    m_editor->deleteLater();
+}
+
+void MemoryViewAgent::fetchLazyData(int block, bool sync)
+{
+    Q_UNUSED(sync); // FIXME: needed support for incremental searching
+    m_engine->fetchMemory(this, BinBlockSize * block, BinBlockSize); 
+}
+
+void MemoryViewAgent::addLazyData(quint64 addr, const QByteArray &ba)
+{
+    QMetaObject::invokeMethod(m_editor->widget(), "addLazyData",
+        Q_ARG(int, addr / BinBlockSize), Q_ARG(QByteArray, ba));
+}
+
+
+} // namespace Internal
+} // namespace Debugger
+
diff --git a/src/plugins/debugger/debuggeragents.h b/src/plugins/debugger/debuggeragents.h
new file mode 100644
index 00000000000..f4a5bf1c54d
--- /dev/null
+++ b/src/plugins/debugger/debuggeragents.h
@@ -0,0 +1,80 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#ifndef DEBUGGER_AGENTS_H
+#define DEBUGGER_AGENTS_H
+
+#include "debuggermanager.h"
+
+#include <coreplugin/icore.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/editormanager/ieditor.h>
+
+#include <utils/qtcassert.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QDebug>
+#include <QtCore/QPointer>
+#include <QtGui/QAction>
+
+namespace Debugger {
+namespace Internal {
+
+class DebuggerManager;
+
+// Object form this class are created in response to user actions in
+// the Gui for showing raw memory from the inferior. After creation
+// it handles communication between the engine and the bineditor.
+
+class MemoryViewAgent : public QObject
+{
+    Q_OBJECT
+
+public:
+    // Called from Gui
+    MemoryViewAgent(DebuggerManager *manager, quint64 startaddr);
+    ~MemoryViewAgent();
+
+    enum { BinBlockSize = 1024 };
+
+public slots:
+    // Called from Engine
+    void addLazyData(quint64 addr, const QByteArray &data);
+    // Called from Editor
+    void fetchLazyData(int block, bool sync);
+
+public:
+    QPointer<IDebuggerEngine> m_engine;
+    QPointer<Core::IEditor> m_editor;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_WATCHWINDOW_H
diff --git a/src/plugins/debugger/debuggerdialogs.h b/src/plugins/debugger/debuggerdialogs.h
index 74dc720e817..ee42ca6cea3 100644
--- a/src/plugins/debugger/debuggerdialogs.h
+++ b/src/plugins/debugger/debuggerdialogs.h
@@ -171,6 +171,7 @@ private:
     Ui::StartRemoteDialog *m_ui;
 };
 
+
 } // namespace Debugger
 } // namespace Internal
 
diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp
index 23a1ae83cda..02907cc5fc2 100644
--- a/src/plugins/debugger/debuggermanager.cpp
+++ b/src/plugins/debugger/debuggermanager.cpp
@@ -226,9 +226,9 @@ void DebuggerManager::init()
     m_stackWindow = new StackWindow;
     m_sourceFilesWindow = new SourceFilesWindow;
     m_threadsWindow = new ThreadsWindow;
-    m_localsWindow = new WatchWindow(WatchWindow::LocalsType);
-    m_watchersWindow = new WatchWindow(WatchWindow::WatchersType);
-    //m_tooltipWindow = new WatchWindow(WatchWindow::TooltipType);
+    m_localsWindow = new WatchWindow(WatchWindow::LocalsType, this);
+    m_watchersWindow = new WatchWindow(WatchWindow::WatchersType, this);
+    //m_tooltipWindow = new WatchWindow(WatchWindow::TooltipType, this);
     m_statusTimer = new QTimer(this);
 
     m_mainWindow = new Core::Utils::FancyMainWindow;
@@ -1497,7 +1497,6 @@ bool DebuggerManager::isReverseDebugging() const
     return m_reverseDirectionAction->isChecked();
 }
 
-
 //////////////////////////////////////////////////////////////////////
 //
 // Testing
diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h
index 406a8403971..fe9e4bce17f 100644
--- a/src/plugins/debugger/debuggermanager.h
+++ b/src/plugins/debugger/debuggermanager.h
@@ -251,8 +251,7 @@ private:
 // DebuggerManager
 //
 
-class DebuggerManager : public QObject,
-    public IDebuggerManagerAccessForEngines
+class DebuggerManager : public QObject, public IDebuggerManagerAccessForEngines
 {
     Q_OBJECT
 
@@ -265,9 +264,11 @@ public:
     IDebuggerManagerAccessForEngines *engineInterface();
     Core::Utils::FancyMainWindow *mainWindow() const { return m_mainWindow; }
     QLabel *statusLabel() const { return m_statusLabel; }
+    IDebuggerEngine *currentEngine() const { return m_engine; }
 
 public slots:
-    void startNewDebugger(DebuggerRunControl *runControl, const QSharedPointer<DebuggerStartParameters> &startParameters);
+    void startNewDebugger(DebuggerRunControl *runControl,
+        const QSharedPointer<DebuggerStartParameters> &startParameters);
     void exitDebugger();
 
     virtual QSharedPointer<DebuggerStartParameters> startParameters() const;
@@ -297,6 +298,7 @@ public slots:
     void setBreakpoint(const QString &fileName, int lineNumber);
     void activateFrame(int index);
     void selectThread(int index);
+    void fetchMemory(quint64 addr, quint64 length);
 
     void stepExec();
     void stepOutExec();
@@ -406,15 +408,12 @@ signals:
     void gotoLocationRequested(const QString &file, int line, bool setLocationMarker);
     void resetLocationRequested();
     void currentTextEditorRequested(QString *fileName, int *lineNumber, QObject **ob);
-    void currentMainWindowRequested(QWidget **);
     void sessionValueRequested(const QString &name, QVariant *value);
     void setSessionValueRequested(const QString &name, const QVariant &value);
     void configValueRequested(const QString &name, QVariant *value);
     void setConfigValueRequested(const QString &name, const QVariant &value);
     void applicationOutputAvailable(const QString &output);
 
-public:
-
 private:
     void init();
     void runTest(const QString &fileName);
diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp
index 3539ef2008e..6b4610e49f8 100644
--- a/src/plugins/debugger/debuggerplugin.cpp
+++ b/src/plugins/debugger/debuggerplugin.cpp
@@ -88,6 +88,8 @@
 #include <QtGui/QTextCursor>
 #include <QtGui/QMessageBox>
 
+#include <climits>
+
 using namespace Core;
 using namespace Debugger::Constants;
 using namespace Debugger::Internal;
@@ -407,6 +409,7 @@ void DebuggingHelperOptionPage::updateState()
 } // namespace Internal
 } // namespace Debugger
 
+
 ///////////////////////////////////////////////////////////////////////
 //
 // DebuggerPlugin
@@ -1114,6 +1117,7 @@ void DebuggerPlugin::gotoLocation(const QString &fileName, int lineNumber,
     }
 }
 
+
 void DebuggerPlugin::changeStatus(int status)
 {
     bool startIsContinue = (status == DebuggerInferiorStopped);
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 7881c026595..006b1420dbd 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -34,6 +34,7 @@
 
 #include "watchutils.h"
 #include "debuggeractions.h"
+#include "debuggeragents.h"
 #include "debuggerconstants.h"
 #include "debuggermanager.h"
 #include "debuggertooltip.h"
@@ -3987,6 +3988,40 @@ void GdbEngine::handleWatchPoint(const GdbResultRecord &record, const QVariant &
     }
 }
 
+void GdbEngine::handleFetchMemory(const GdbResultRecord &record,
+    const QVariant &cookie)
+{
+    // ^done,addr="0x08910c88",nr-bytes="16",total-bytes="16",
+    // next-row="0x08910c98",prev-row="0x08910c78",next-page="0x08910c98",
+    // prev-page="0x08910c78",memory=[{addr="0x08910c88",
+    // data=["1","0","0","0","5","0","0","0","0","0","0","0","0","0","0","0"]}]
+    bool ok = true;
+    MemoryViewAgent *agent = (MemoryViewAgent *)cookie.toULongLong(&ok);
+    QTC_ASSERT(ok, return);
+    QTC_ASSERT(agent, return);
+    QByteArray ba;
+    GdbMi memory = record.data.findChild("memory");
+    QTC_ASSERT(memory.children().size() == 1, return);
+    GdbMi memory0 = memory.children().at(0); // we asked for only one 'row'
+    quint64 addr = memory0.findChild("addr").data().toULongLong(&ok, 0);
+    QTC_ASSERT(ok, return);
+    GdbMi data = memory0.findChild("data");
+    foreach (const GdbMi &child, data.children()) {
+        unsigned char c = child.data().toUInt(&ok, 0);
+        QTC_ASSERT(ok, return);
+        ba.append(c);
+    }
+    //qDebug() << "GDB READ MEMORY" << agent << addr << data.data() << ba.size();
+    agent->addLazyData(addr, ba);
+}
+
+void GdbEngine::fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 length)
+{
+    //qDebug() << "GDB MEMORY FETCH" << addr << length;
+    postCommand(_("-data-read-memory %1 x 1 1 %2").arg(addr).arg(length),
+        NeedsStop, CB(handleFetchMemory), QVariant(quint64(agent)));
+}
+
 IDebuggerEngine *createGdbEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *opts)
 {
     opts->push_back(new GdbOptionsPage);
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index c8ff6a6698d..7fb115ce967 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -122,6 +122,9 @@ private:
     void loadAllSymbols();
     virtual QList<Symbol> moduleSymbols(const QString &moduleName);
 
+    void fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 length);
+    void handleFetchMemory(const GdbResultRecord &record, const QVariant &cookie);
+
     Q_SLOT void setDebugDebuggingHelpers(const QVariant &on);
     Q_SLOT void setUseDebuggingHelpers(const QVariant &on);
 
diff --git a/src/plugins/debugger/idebuggerengine.h b/src/plugins/debugger/idebuggerengine.h
index dbbdb143143..0ca38a14b0c 100644
--- a/src/plugins/debugger/idebuggerengine.h
+++ b/src/plugins/debugger/idebuggerengine.h
@@ -49,6 +49,7 @@ namespace Internal {
 class Symbol;
 class WatchData;
 struct DebuggerStartParameters;
+class MemoryViewAgent;
 
 class IDebuggerEngine : public QObject
 {
@@ -95,6 +96,8 @@ public:
     virtual void reloadFullStack() = 0;
 
     virtual void watchPoint(const QPoint &) {}
+    virtual void fetchMemory(MemoryViewAgent *, quint64 addr, quint64 length)
+        { Q_UNUSED(addr); Q_UNUSED(length); }
 };
 
 } // namespace Internal
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
index fad2dfa77f4..f902ed3d1d1 100644
--- a/src/plugins/debugger/watchhandler.cpp
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -690,6 +690,16 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
                 return m_handler->m_typeFormats[data.type];
             return format;
         }
+        
+        case AddressRole: {
+            if (!data.addr.isEmpty())
+                return data.addr;
+            bool ok;
+            (void) data.value.toULongLong(&ok, 0);
+            if (ok)
+                return data.value;
+            return QVariant();
+        }
 
         default:
             break; 
diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h
index 0778ec25451..f1da9d96f6d 100644
--- a/src/plugins/debugger/watchhandler.h
+++ b/src/plugins/debugger/watchhandler.h
@@ -146,7 +146,8 @@ enum WatchRoles
     ActiveDataRole,  // used for tooltip
     TypeFormatListRole,
     TypeFormatRole,  // used to communicate alternative formats to the view
-    IndividualFormatRole
+    IndividualFormatRole,
+    AddressRole,     // some memory address related to the object
 };
 
 enum IntegerFormat
diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp
index 571e8ac4b9b..d024582739a 100644
--- a/src/plugins/debugger/watchwindow.cpp
+++ b/src/plugins/debugger/watchwindow.cpp
@@ -31,6 +31,7 @@
 #include "watchhandler.h"
 
 #include "debuggeractions.h"
+#include "debuggeragents.h"
 
 #include <utils/qtcassert.h>
 
@@ -39,8 +40,11 @@
 
 #include <QtGui/QAction>
 #include <QtGui/QContextMenuEvent>
+#include <QtGui/QDialog>
+#include <QtGui/QHBoxLayout>
 #include <QtGui/QHeaderView>
 #include <QtGui/QItemDelegate>
+#include <QtGui/QLabel>
 #include <QtGui/QLineEdit>
 #include <QtGui/QMenu>
 #include <QtGui/QResizeEvent>
@@ -111,8 +115,9 @@ public:
 //
 /////////////////////////////////////////////////////////////////////
 
-WatchWindow::WatchWindow(Type type, QWidget *parent)
-    : QTreeView(parent), m_alwaysResizeColumnsToContents(true), m_type(type)
+WatchWindow::WatchWindow(Type type, DebuggerManager *manager, QWidget *parent)
+    : QTreeView(parent), m_alwaysResizeColumnsToContents(true), m_type(type),
+        m_manager(manager)
 {
     m_grabbing = false;
 
@@ -237,28 +242,44 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
     }
 
     QMenu menu;
-    QAction *act1 = new QAction(tr("Adjust column widths to contents"), &menu);
-    QAction *act2 = new QAction(tr("Always adjust column widths to contents"), &menu);
-
-    act2->setCheckable(true);
-    act2->setChecked(m_alwaysResizeColumnsToContents);
-
-    //QAction *act4 = theDebuggerAction(WatchExpressionInWindow);
-    //menu.addAction(act4);
-
-    QAction *act3 = new QAction(tr("Insert new watch item"), &menu); 
-    QAction *act4 = new QAction(tr("Select widget to watch"), &menu);
-
-    menu.addAction(act1);
-    menu.addAction(act2);
+    QAction *actAdjustColumnWidths =
+        new QAction(tr("Adjust column widths to contents"), &menu);
+
+    QAction *actAlwaysAdjustColumnWidth =
+        new QAction(tr("Always adjust column widths to contents"), &menu);
+    actAlwaysAdjustColumnWidth->setCheckable(true);
+    actAlwaysAdjustColumnWidth->setChecked(m_alwaysResizeColumnsToContents);
+
+    //QAction *actWatchExpressionInWindow
+    // = theDebuggerAction(WatchExpressionInWindow);
+    //menu.addAction(actWatchExpressionInWindow);
+
+    QAction *actInsertNewWatchItem =
+        new QAction(tr("Insert new watch item"), &menu); 
+
+    QAction *actSelectWidgetToWatch =
+        new QAction(tr("Select widget to watch"), &menu);
+
+    QString address = model()->data(mi0, AddressRole).toString();
+    QAction *actWatchKnownMemory = 0;
+    QAction *actWatchUnknownMemory = 0;
+    if (address.isEmpty())
+        actWatchUnknownMemory = new QAction(tr("Open memory editor"), &menu);
+    else
+        actWatchKnownMemory =
+            new QAction(tr("Open memory editor at %1").arg(address), &menu);
+
+    menu.addAction(actAdjustColumnWidths);
+    menu.addAction(actAlwaysAdjustColumnWidth);
     menu.addSeparator();
     int atype = (m_type == LocalsType) ? WatchExpression : RemoveWatchExpression;
     menu.addAction(theDebuggerAction(atype)->updatedAction(exp));
 
-    menu.addAction(act3);
-    menu.addAction(act4);
+    menu.addAction(actInsertNewWatchItem);
+    menu.addAction(actSelectWidgetToWatch);
     menu.addMenu(&typeFormatMenu);
     menu.addMenu(&individualFormatMenu);
+    menu.addAction(actWatchKnownMemory ? actWatchKnownMemory : actWatchUnknownMemory);
 
     menu.addSeparator();
     menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
@@ -268,14 +289,33 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
 
     QAction *act = menu.exec(ev->globalPos());
 
-    if (act == act1) {
+    if (act == actAdjustColumnWidths) {
         resizeColumnsToContents();
-    } else if (act == act2) {
+    } else if (act == actAlwaysAdjustColumnWidth) {
         setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
-    } else if (act == act3) {
+    } else if (act == actInsertNewWatchItem) {
         theDebuggerAction(WatchExpression)
             ->trigger(WatchHandler::watcherEditPlaceHolder());
-    } else if (act == act4) {
+    } else if (act == actWatchKnownMemory) {
+        bool ok = true;
+        uint addr = address.toUInt(&ok, 0);
+        (void) new MemoryViewAgent(m_manager, addr);
+    } else if (act == actWatchUnknownMemory) {
+        QLabel *label = new QLabel("Enter an address: ");
+        QLineEdit *lineEdit = new QLineEdit;
+        QHBoxLayout *layout = new QHBoxLayout;
+        layout->addWidget(label);
+        layout->addWidget(lineEdit);
+        QDialog dialog(this);
+        dialog.setWindowTitle("Select start address");
+        dialog.setLayout(layout);
+        connect(lineEdit, SIGNAL(returnPressed()), &dialog, SLOT(accept()));
+        if (dialog.exec() == QDialog::Accepted) {
+            bool ok = true;
+            uint addr = lineEdit->text().toUInt(&ok, 0);
+            (void) new MemoryViewAgent(m_manager, addr);
+        }
+    } else if (act == actSelectWidgetToWatch) {
         grabMouse(Qt::CrossCursor);
         m_grabbing = true;
     } else { 
diff --git a/src/plugins/debugger/watchwindow.h b/src/plugins/debugger/watchwindow.h
index e937139276d..105af278b30 100644
--- a/src/plugins/debugger/watchwindow.h
+++ b/src/plugins/debugger/watchwindow.h
@@ -41,6 +41,8 @@ namespace Internal {
 //
 /////////////////////////////////////////////////////////////////////
 
+class DebuggerManager;
+
 class WatchWindow : public QTreeView
 {
     Q_OBJECT
@@ -48,7 +50,7 @@ class WatchWindow : public QTreeView
 public:
     enum Type { LocalsType, TooltipType, WatchersType };
 
-    WatchWindow(Type type, QWidget *parent = 0);
+    WatchWindow(Type type, DebuggerManager *manager, QWidget *parent = 0);
     void setType(Type type) { m_type = type; }
     Type type() const { return m_type; }
     
@@ -75,6 +77,7 @@ private:
 
     bool m_alwaysResizeColumnsToContents;
     Type m_type;
+    DebuggerManager *m_manager;
     bool m_grabbing;
 };
 
-- 
GitLab