memoryagent.cpp 10.9 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
Eike Ziller's avatar
Eike Ziller committed
3
4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
Eike Ziller's avatar
Eike Ziller committed
12
13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25
26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
30
31

#include "memoryagent.h"
32
#include "memoryview.h"
33

34
#include "breakhandler.h"
35
#include "debuggerengine.h"
36
#include "debuggerstartparameters.h"
37
#include "debuggercore.h"
38
#include "debuggerinternalconstants.h"
39
40

#include <coreplugin/coreconstants.h>
41
#include <coreplugin/editormanager/ieditor.h>
42
#include <coreplugin/editormanager/editormanager.h>
43
#include <coreplugin/idocument.h>
44
#include <coreplugin/messagebox.h>
45
46

#include <utils/qtcassert.h>
47
48
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/invoker.h>
49

50
#include <cstring>
51
52
53
54
55
56
57
58

using namespace Core;

namespace Debugger {
namespace Internal {

///////////////////////////////////////////////////////////////////////
//
59
// MemoryAgent
60
61
62
63
//
///////////////////////////////////////////////////////////////////////

/*!
64
    \class Debugger::Internal::MemoryAgent
65
66
67
68

    Objects 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.
69
70
71

    Memory can be shown as
    \list
72
    \li Editor: Create an IEditor using the normal editor factory
73
       interface (m_editors)
74
    \li View: Separate top-level view consisting of a Bin Editor widget
75
76
77
       (m_view).
    \endlist

hjk's avatar
hjk committed
78
79
    Views are asked to update themselves directly by the owning
    DebuggerEngine.
80
81
82
83
84
85
    An exception are views of class Debugger::RegisterMemoryView tracking the
    content pointed to by a register (eg stack pointer, instruction pointer).
    They are connected to the set/changed signals of
    the engine's register handler.

    \sa Debugger::MemoryView,  Debugger::RegisterMemoryView
86
87
*/

88
MemoryAgent::MemoryAgent(DebuggerEngine *engine)
89
90
    : QObject(engine), m_engine(engine)
{
91
    QTC_CHECK(engine);
92
93
    connect(engine, &DebuggerEngine::stackFrameCompleted,
            this, &MemoryAgent::updateContents);
94
95
}

96
MemoryAgent::~MemoryAgent()
97
{
98
99
100
101
102
103
104
105
106
    closeEditors();
    closeViews();
}

void MemoryAgent::closeEditors()
{
    if (m_editors.isEmpty())
        return;

107
    QSet<IDocument *> documents;
108
109
    foreach (QPointer<IEditor> editor, m_editors)
        if (editor)
110
111
            documents.insert(editor->document());
    EditorManager::closeDocuments(documents.toList());
112
    m_editors.clear();
113
114
}

115
void MemoryAgent::closeViews()
116
{
117
118
119
120
    foreach (const QPointer<MemoryView> &w, m_views)
        if (w)
            w->close();
    m_views.clear();
121
122
}

123
void MemoryAgent::updateMemoryView(quint64 address, quint64 length)
124
{
125
    m_engine->fetchMemory(this, sender(), address, length);
126
127
}

128
void MemoryAgent::connectBinEditorWidget(QWidget *w)
129
{
130
131
132
133
134
135
    connect(w, SIGNAL(dataRequested(quint64)), SLOT(fetchLazyData(quint64)));
    connect(w, SIGNAL(newWindowRequested(quint64)), SLOT(createBinEditor(quint64)));
    connect(w, SIGNAL(newRangeRequested(quint64)), SLOT(provideNewRange(quint64)));
    connect(w, SIGNAL(dataChanged(quint64,QByteArray)), SLOT(handleDataChanged(quint64,QByteArray)));
    connect(w, SIGNAL(dataChanged(quint64,QByteArray)), SLOT(handleDataChanged(quint64,QByteArray)));
    connect(w, SIGNAL(addWatchpointRequested(quint64,uint)), SLOT(handleWatchpointRequest(quint64,uint)));
136
137
}

hjk's avatar
hjk committed
138
bool MemoryAgent::doCreateBinEditor(const MemoryViewSetupData &data)
139
{
hjk's avatar
hjk committed
140
141
    const bool readOnly = (data.flags & DebuggerEngine::MemoryReadOnly) != 0;
    QString title = data.title.isEmpty() ? tr("Memory at 0x%1").arg(data.startAddress, 0, 16) : data.title;
142
    // Separate view?
hjk's avatar
hjk committed
143
    if (data.flags & DebuggerEngine::MemoryView) {
144
145
        // Ask BIN editor plugin for factory service and have it create a bin editor widget.
        QWidget *binEditor = 0;
hjk's avatar
hjk committed
146
        if (QObject *factory = ExtensionSystem::PluginManager::getObjectByClassName(QLatin1String("BinEditor::BinEditorWidgetFactory")))
147
148
149
150
151
152
153
154
            binEditor = ExtensionSystem::invoke<QWidget *>(factory, "createWidget", (QWidget *)0);
        if (!binEditor)
            return false;
        connectBinEditorWidget(binEditor);
        MemoryView::setBinEditorReadOnly(binEditor, readOnly);
        MemoryView::setBinEditorNewWindowRequestAllowed(binEditor, true);
        MemoryView *topLevel = 0;
        // Memory view tracking register value, providing its own updating mechanism.
hjk's avatar
hjk committed
155
156
        if (data.flags & DebuggerEngine::MemoryTrackRegister) {
            topLevel = new RegisterMemoryView(binEditor, data.startAddress, data.registerName, m_engine->registerHandler(), data.parent);
157
158
        } else {
            // Ordinary memory view
hjk's avatar
hjk committed
159
160
161
            MemoryView::setBinEditorMarkup(binEditor, data.markup);
            MemoryView::setBinEditorRange(binEditor, data.startAddress, MemoryAgent::DataRange, MemoryAgent::BinBlockSize);
            topLevel = new MemoryView(binEditor, data.parent);
162
163
164
            topLevel->setWindowTitle(title);
        }
        m_views << topLevel;
hjk's avatar
hjk committed
165
        topLevel->move(data.pos);
166
167
168
169
        topLevel->show();
        return true;
    }
    // Editor: Register tracking not supported.
hjk's avatar
hjk committed
170
    QTC_ASSERT(!(data.flags & DebuggerEngine::MemoryTrackRegister), return false);
171
172
    if (!title.endsWith(QLatin1Char('$')))
        title.append(QLatin1String(" $"));
hjk's avatar
hjk committed
173
    IEditor *editor = EditorManager::openEditorWithContents(
174
175
176
                Core::Constants::K_DEFAULT_BINARY_EDITOR_ID, &title);
    if (!editor)
        return false;
177
    editor->document()->setProperty(Constants::OPENED_BY_DEBUGGER, QVariant(true));
178
    editor->document()->setTemporary(true);
179
180
181
182
    QWidget *editorBinEditor = editor->widget();
    connectBinEditorWidget(editorBinEditor);
    MemoryView::setBinEditorReadOnly(editorBinEditor, readOnly);
    MemoryView::setBinEditorNewWindowRequestAllowed(editorBinEditor, true);
hjk's avatar
hjk committed
183
184
    MemoryView::setBinEditorRange(editorBinEditor, data.startAddress, MemoryAgent::DataRange, MemoryAgent::BinBlockSize);
    MemoryView::setBinEditorMarkup(editorBinEditor, data.markup);
185
186
187
188
    m_editors << editor;
    return true;
}

hjk's avatar
hjk committed
189
void MemoryAgent::createBinEditor(const MemoryViewSetupData &data)
190
{
hjk's avatar
hjk committed
191
    if (!doCreateBinEditor(data))
192
        AsynchronousMessageBox::warning(
193
            tr("No Memory Viewer Available"),
194
195
196
197
            tr("The memory contents cannot be shown as no viewer plugin "
               "for binary data has been loaded."));
}

198
void MemoryAgent::createBinEditor(quint64 addr)
199
{
hjk's avatar
hjk committed
200
201
202
    MemoryViewSetupData data;
    data.startAddress = addr;
    createBinEditor(data);
203
204
}

205
void MemoryAgent::fetchLazyData(quint64 block)
206
207
{
    m_engine->fetchMemory(this, sender(), BinBlockSize * block, BinBlockSize);
208
209
}

210
void MemoryAgent::addLazyData(QObject *editorToken, quint64 addr,
211
212
                                  const QByteArray &ba)
{
213
    QWidget *w = qobject_cast<QWidget *>(editorToken);
214
    QTC_ASSERT(w, return);
215
    MemoryView::binEditorAddData(w, addr, ba);
216
217
}

218
void MemoryAgent::provideNewRange(quint64 address)
219
{
220
    QWidget *w = qobject_cast<QWidget *>(sender());
221
    QTC_ASSERT(w, return);
222
    MemoryView::setBinEditorRange(w, address, DataRange, BinBlockSize);
223
224
}

225
void MemoryAgent::handleDataChanged(quint64 addr, const QByteArray &data)
hjk's avatar
hjk committed
226
{
227
    m_engine->changeMemory(this, sender(), addr, data);
hjk's avatar
hjk committed
228
229
}

230
231
232
233
234
void MemoryAgent::handleWatchpointRequest(quint64 address, uint size)
{
    m_engine->breakHandler()->setWatchpointAtAddress(address, size);
}

235
void MemoryAgent::updateContents()
236
{
237
    foreach (const QPointer<IEditor> &e, m_editors)
238
239
240
241
242
243
244
        if (e)
            MemoryView::binEditorUpdateContents(e->widget());
    // Update all views except register views, which trigger on
    // register value set/changed.
    foreach (const QPointer<MemoryView> &w, m_views)
        if (w && !qobject_cast<RegisterMemoryView *>(w.data()))
            w->updateContents();
245
246
}

247
248
bool MemoryAgent::hasVisibleEditor() const
{
249
    QList<IEditor *> visible = EditorManager::visibleEditors();
250
251
252
253
254
255
    foreach (QPointer<IEditor> editor, m_editors)
        if (visible.contains(editor.data()))
            return true;
    return false;
}

256
void MemoryAgent::handleDebuggerFinished()
257
{
258
259
260
261
262
    foreach (const QPointer<IEditor> &editor, m_editors) {
        if (editor) { // Prevent triggering updates, etc.
            MemoryView::setBinEditorReadOnly(editor->widget(), true);
            editor->widget()->disconnect(this);
        }
263
264
265
    }
}

266
267
268
269
270
271
272
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
Tobias Hunger's avatar
Tobias Hunger committed
273
    case ProjectExplorer::Abi::ShArchitecture:     // Configureable
274
        break;
Tobias Hunger's avatar
Tobias Hunger committed
275
    case ProjectExplorer::Abi::MipsArchitecture:     // Configureable
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
    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);
}

304
305
} // namespace Internal
} // namespace Debugger
306
;