From abf5e3ddc39b8f4971f61fb9f8dccdb1d4635bf5 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <Friedemann.Kleint@nokia.com> Date: Fri, 10 Jul 2009 14:36:28 +0200 Subject: [PATCH] Enabled the use of Debugger-specific watch/locals models. - Modified WatchModel to handle storage of an hierarchy of WatchItems. - Factored out code for asynchronous population to AsyncWatchModel and added a mixin for convenient handling - Added base class for synchronous models. - Implement simple, synchronous models for CDB, greatly simplifying code and finally getting manual expansion right. Signed-off-by: hjk <qtc-committer@nokia.com> --- .../debugger/abstractsyncwatchmodel.cpp | 107 +++ ...ramecontext.h => abstractsyncwatchmodel.h} | 54 +- src/plugins/debugger/asyncwatchmodel.cpp | 232 ++++++ src/plugins/debugger/asyncwatchmodel.h | 102 +++ src/plugins/debugger/cdb/cdb.pri | 8 +- src/plugins/debugger/cdb/cdbdebugengine.cpp | 178 +++-- src/plugins/debugger/cdb/cdbdebugengine.h | 16 +- src/plugins/debugger/cdb/cdbdebugengine_p.h | 13 +- .../debugger/cdb/cdbstacktracecontext.cpp | 55 +- .../debugger/cdb/cdbstacktracecontext.h | 17 +- .../debugger/cdb/cdbsymbolgroupcontext.cpp | 1 - .../debugger/cdb/cdbsymbolgroupcontext.h | 25 - .../debugger/cdb/cdbsymbolgroupcontext_tpl.h | 99 --- src/plugins/debugger/cdb/cdbwatchmodels.cpp | 477 +++++++++++++ src/plugins/debugger/cdb/cdbwatchmodels.h | 96 +++ src/plugins/debugger/debugger.pro | 4 + src/plugins/debugger/debuggermanager.cpp | 58 +- src/plugins/debugger/debuggermanager.h | 8 +- src/plugins/debugger/gdb/gdbengine.cpp | 20 +- src/plugins/debugger/gdb/gdbengine.h | 6 +- src/plugins/debugger/idebuggerengine.h | 7 +- src/plugins/debugger/script/scriptengine.cpp | 21 +- src/plugins/debugger/script/scriptengine.h | 9 +- src/plugins/debugger/tcf/tcfengine.cpp | 10 +- src/plugins/debugger/tcf/tcfengine.h | 9 +- src/plugins/debugger/watchhandler.cpp | 661 +++++++++--------- src/plugins/debugger/watchhandler.h | 151 ++-- src/plugins/debugger/watchutils.cpp | 23 +- src/plugins/debugger/watchwindow.cpp | 8 +- 29 files changed, 1788 insertions(+), 687 deletions(-) create mode 100644 src/plugins/debugger/abstractsyncwatchmodel.cpp rename src/plugins/debugger/{cdb/cdbstackframecontext.h => abstractsyncwatchmodel.h} (52%) create mode 100644 src/plugins/debugger/asyncwatchmodel.cpp create mode 100644 src/plugins/debugger/asyncwatchmodel.h create mode 100644 src/plugins/debugger/cdb/cdbwatchmodels.cpp create mode 100644 src/plugins/debugger/cdb/cdbwatchmodels.h diff --git a/src/plugins/debugger/abstractsyncwatchmodel.cpp b/src/plugins/debugger/abstractsyncwatchmodel.cpp new file mode 100644 index 00000000000..9cf1d8404dd --- /dev/null +++ b/src/plugins/debugger/abstractsyncwatchmodel.cpp @@ -0,0 +1,107 @@ +/************************************************************************** +** +** 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 "abstractsyncwatchmodel.h" + +#include <utils/qtcassert.h> +#include <QtCore/QDebug> + +namespace Debugger { +namespace Internal { + +enum { debug = 0 }; + +AbstractSyncWatchModel::AbstractSyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent) : + WatchModel(handler, type, parent) +{ +} + +void AbstractSyncWatchModel::fetchMore(const QModelIndex &parent) +{ + WatchItem *item = watchItem(parent); + if (!item || item == root()) + return; + if (debug) + qDebug() << ">fetchMore" << item->toString(); + // Figure out children... + if (item->isHasChildrenNeeded()) { + m_errorMessage.clear(); + if (!complete(item, &m_errorMessage)) { + item->setHasChildren(false); + emit error(m_errorMessage); + } + } + if (item->isChildrenNeeded()) { + m_errorMessage.clear(); + if (fetchChildren(item, &m_errorMessage)) { + item->setChildrenUnneeded(); + } else { + item->setHasChildren(false); + emit error(m_errorMessage); + } + } + + if (debug) + qDebug() << "<fetchMore" << item->iname << item->children.size(); +} + +bool AbstractSyncWatchModel::canFetchMore(const QModelIndex &parent) const +{ + WatchItem *item = watchItem(parent); + if (!item || item == root()) + return false; + const bool rc = item->isChildrenNeeded() || item->isHasChildrenNeeded(); + if (debug) + qDebug() << "canFetchMore" << rc << item->iname; + return rc; +} + +QVariant AbstractSyncWatchModel::data(const QModelIndex &idx, int role) const +{ + if (WatchItem *wdata = watchItem(idx)) { + const int column = idx.column(); + // Is any display data (except children) needed? + const bool incomplete = (wdata->state & ~WatchData::ChildrenNeeded); + if ((debug > 1) && incomplete && column == 0 && role == Qt::DisplayRole) + qDebug() << "data()" << "incomplete=" << incomplete << wdata->toString(); + if (incomplete) { + AbstractSyncWatchModel *nonConstThis = const_cast<AbstractSyncWatchModel *>(this); + m_errorMessage.clear(); + if (!nonConstThis->complete(wdata, &m_errorMessage)) { + wdata->setAllUnneeded(); + nonConstThis->emit error(m_errorMessage); + } + } + return WatchModel::data(*wdata, column, role); + } + return QVariant(); +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbstackframecontext.h b/src/plugins/debugger/abstractsyncwatchmodel.h similarity index 52% rename from src/plugins/debugger/cdb/cdbstackframecontext.h rename to src/plugins/debugger/abstractsyncwatchmodel.h index f9ba79c9f83..91573a0602b 100644 --- a/src/plugins/debugger/cdb/cdbstackframecontext.h +++ b/src/plugins/debugger/abstractsyncwatchmodel.h @@ -27,49 +27,49 @@ ** **************************************************************************/ -#ifndef CDBSTACKFRAMECONTEXT_H -#define CDBSTACKFRAMECONTEXT_H +#ifndef ABSTRACTSYNCWATCHMODEL_H +#define ABSTRACTSYNCWATCHMODEL_H +#include "watchhandler.h" +#include <QtCore/QPointer> #include <QtCore/QList> -#include <QtCore/QSharedPointer> + +QT_BEGIN_NAMESPACE +class QDebug; +QT_END_NAMESPACE namespace Debugger { namespace Internal { -class WatchData; -class WatchHandler; -class CdbSymbolGroupContext; -class CdbDumperHelper; - -/* CdbStackFrameContext manages a symbol group context and - * a dumper context. It dispatches calls between the local items - * that are handled by the symbol group and those that are handled by the dumpers. */ +/* AbstractSyncWatchModel: To be used by synchonous debuggers. + * Implements all of WatchModel and provides new virtuals for + * the debugger to complete items. */ -class CdbStackFrameContext +class AbstractSyncWatchModel : public WatchModel { - Q_DISABLE_COPY(CdbStackFrameContext) + Q_OBJECT public: - explicit CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper, - CdbSymbolGroupContext *symbolContext); - ~CdbStackFrameContext(); + explicit AbstractSyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent = 0); + + virtual QVariant data(const QModelIndex &index, int role) const; - bool assignValue(const QString &iname, const QString &value, - QString *newValue /* = 0 */, QString *errorMessage); - bool editorToolTip(const QString &iname, QString *value, QString *errorMessage); + virtual void fetchMore(const QModelIndex &parent); + virtual bool canFetchMore(const QModelIndex &parent) const; - bool populateModelInitially(WatchHandler *wh, QString *errorMessage); +signals: + // Emitted if one of fetchChildren/complete fails. + void error(const QString &); - bool completeData(const WatchData &incompleteLocal, - WatchHandler *wh, - QString *errorMessage); +protected: + // Overwrite these virtuals to fetch children of an item and to complete it + virtual bool fetchChildren(WatchItem *wd, QString *errorMessage) = 0; + virtual bool complete(WatchItem *wd, QString *errorMessage) = 0; private: - const bool m_useDumpers; - const QSharedPointer<CdbDumperHelper> m_dumper; - CdbSymbolGroupContext *m_symbolContext; + mutable QString m_errorMessage; }; } // namespace Internal } // namespace Debugger -#endif // CDBSTACKFRAMECONTEXT_H +#endif // ABSTRACTSYNCWATCHMODEL_H diff --git a/src/plugins/debugger/asyncwatchmodel.cpp b/src/plugins/debugger/asyncwatchmodel.cpp new file mode 100644 index 00000000000..5b627eb8606 --- /dev/null +++ b/src/plugins/debugger/asyncwatchmodel.cpp @@ -0,0 +1,232 @@ +/************************************************************************** +** +** 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 "asyncwatchmodel.h" + +#include <QtCore/QCoreApplication> + +#include <utils/qtcassert.h> + +namespace Debugger { +namespace Internal { + +int AsyncWatchModel::generationCounter = 0; + +static const QString strNotInScope = + QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>"); + +//////////////////////////////////////////////////////////////////// +// +// WatchItem +// +//////////////////////////////////////////////////////////////////// + +AsyncWatchModel::AsyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent) : + WatchModel(handler, type, parent) +{ + dummyRoot()->fetchTriggered = true; +} + +void AsyncWatchModel::removeOutdated() +{ + WatchItem *item = dummyRoot(); + QTC_ASSERT(item, return); + foreach (WatchItem *child, item->children) + removeOutdatedHelper(child); +#if DEBUG_MODEL +#if USE_MODEL_TEST + //(void) new ModelTest(this, this); +#endif +#endif +} + +void AsyncWatchModel::removeOutdatedHelper(WatchItem *item) +{ + if (item->generation < generationCounter) + removeItem(item); + else { + foreach (WatchItem *child, item->children) + removeOutdatedHelper(child); + item->fetchTriggered = false; + } +} + +bool AsyncWatchModel::canFetchMore(const QModelIndex &index) const +{ + if (index.isValid()) + if (const WatchItem *item = watchItem(index)) + return !item->fetchTriggered; + return false; +} + +void AsyncWatchModel::fetchMore(const QModelIndex &index) +{ + QTC_ASSERT(index.isValid(), return); + WatchItem *item = watchItem(index); + QTC_ASSERT(item && !item->fetchTriggered, return); + item->fetchTriggered = true; + WatchData data = *item; + data.setChildrenNeeded(); + emit watchDataUpdateNeeded(data); +} + +static bool iNameSorter(const WatchItem *item1, const WatchItem *item2) +{ + QString name1 = item1->iname.section('.', -1); + QString name2 = item2->iname.section('.', -1); + if (!name1.isEmpty() && !name2.isEmpty()) { + if (name1.at(0).isDigit() && name2.at(0).isDigit()) + return name1.toInt() < name2.toInt(); + } + return name1 < name2; +} + +static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item) +{ + QList<WatchItem *>::const_iterator it = + qLowerBound(list.begin(), list.end(), item, iNameSorter); + return it - list.begin(); +} + +void AsyncWatchModel::insertData(const WatchData &data) +{ + // qDebug() << "WMI:" << data.toString(); + QTC_ASSERT(!data.iname.isEmpty(), return); + WatchItem *parent = findItemByIName(parentName(data.iname), root()); + if (!parent) { + WatchData parent; + parent.iname = parentName(data.iname); + insertData(parent); + //MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname); + return; + } + QModelIndex index = watchIndex(parent); + if (WatchItem *oldItem = findItemByIName(data.iname, parent)) { + // overwrite old entry + //MODEL_DEBUG("OVERWRITE : " << data.iname << data.value); + bool changed = !data.value.isEmpty() + && data.value != oldItem->value + && data.value != strNotInScope; + oldItem->setData(data); + oldItem->changed = changed; + oldItem->generation = generationCounter; + QModelIndex idx = watchIndex(oldItem); + emit dataChanged(idx, idx.sibling(idx.row(), 2)); + } else { + // add new entry + //MODEL_DEBUG("INSERT : " << data.iname << data.value); + WatchItem *item = new WatchItem(data); + item->parent = parent; + item->generation = generationCounter; + item->changed = true; + int n = findInsertPosition(parent->children, item); + beginInsertRows(index, n, n); + parent->children.insert(n, item); + endInsertRows(); + } +} + +// ----------------- AsyncWatchModelMixin +AsyncWatchModelMixin::AsyncWatchModelMixin(WatchHandler *wh, + QObject *parent) : + QObject(parent), + m_wh(wh) +{ +} + +AsyncWatchModelMixin::~AsyncWatchModelMixin() +{ +} + +AsyncWatchModel *AsyncWatchModelMixin::model(int wt) const +{ + if (wt < 0 || wt >= WatchModelCount) + return 0; + if (!m_models[wt]) { + m_models[wt] = new AsyncWatchModel(m_wh, static_cast<WatchType>(wt), const_cast<AsyncWatchModelMixin *>(this)); + connect(m_models[wt], SIGNAL(watchDataUpdateNeeded(WatchData)), this, SIGNAL(watchDataUpdateNeeded(WatchData))); + m_models[wt]->setObjectName(QLatin1String("model") + QString::number(wt)); + } + return m_models[wt]; +} + +AsyncWatchModel *AsyncWatchModelMixin::modelForIName(const QString &iname) const +{ + const WatchType wt = WatchHandler::watchTypeOfIName(iname); + QTC_ASSERT(wt != WatchModelCount, return 0); + return model(wt); +} + +void AsyncWatchModelMixin::beginCycle() +{ + ++AsyncWatchModel::generationCounter; +} + +void AsyncWatchModelMixin::updateWatchers() +{ + //qDebug() << "UPDATE WATCHERS"; + // copy over all watchers and mark all watchers as incomplete + foreach (const QString &exp, m_wh->watcherExpressions()) { + WatchData data; + data.iname = m_wh->watcherName(exp); + data.setAllNeeded(); + data.name = exp; + data.exp = exp; + insertData(data); + } +} + +void AsyncWatchModelMixin::endCycle() +{ + for (int m = 0; m < WatchModelCount; m++) + if (m_models[m]) + m_models[m]->removeOutdated(); +} + +void AsyncWatchModelMixin::insertWatcher(const WatchData &data) +{ + // Is the engine active? + if (m_models[WatchersWatch] && m_wh->model(WatchersWatch) == m_models[WatchersWatch]) + insertData(data); +} + +void AsyncWatchModelMixin::insertData(const WatchData &data) +{ + QTC_ASSERT(data.isValid(), return); + if (data.isSomethingNeeded()) { + emit watchDataUpdateNeeded(data); + } else { + AsyncWatchModel *model = modelForIName(data.iname); + QTC_ASSERT(model, return); + model->insertData(data); + } +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/asyncwatchmodel.h b/src/plugins/debugger/asyncwatchmodel.h new file mode 100644 index 00000000000..880f2f83d76 --- /dev/null +++ b/src/plugins/debugger/asyncwatchmodel.h @@ -0,0 +1,102 @@ +/************************************************************************** +** +** 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 ASYNCWATCHMODEL_H +#define ASYNCWATCHMODEL_H + +#include "watchhandler.h" +#include <QtCore/QPointer> + +namespace Debugger { +namespace Internal { + +/* AsyncWatchModel: incrementally populated by asynchronous debuggers via + * fetch. */ + +class AsyncWatchModel : public WatchModel +{ + Q_OBJECT +public: + explicit AsyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent = 0); + + bool canFetchMore(const QModelIndex &parent) const; + void fetchMore(const QModelIndex &parent); + + friend class WatchHandler; + friend class GdbEngine; + + void insertData(const WatchData &data); + + void removeOutdated(); + void removeOutdatedHelper(WatchItem *item); + + void setActiveData(const QString &data) { m_activeData = data; } + + static int generationCounter; + +signals: + void watchDataUpdateNeeded(const WatchData &data); + +private: + QString m_activeData; + WatchItem *m_root; +}; + +/* A Mixin to manage asynchronous models. */ + +class AsyncWatchModelMixin : public QObject { + Q_OBJECT +public: + explicit AsyncWatchModelMixin(WatchHandler *wh, QObject *parent = 0); + virtual ~AsyncWatchModelMixin(); + + AsyncWatchModel *model(int t) const; + AsyncWatchModel *modelForIName(const QString &iname) const; + + void beginCycle(); // called at begin of updateLocals() cycle + void updateWatchers(); // called after locals are fetched + void endCycle(); // called after all results have been received + +public slots: + void insertData(const WatchData &data); + // For watchers, ensures that engine is active + void insertWatcher(const WatchData &data); + +signals: + void watchDataUpdateNeeded(const WatchData &data); + +private: + WatchHandler *m_wh; + mutable QPointer<AsyncWatchModel> m_models[WatchModelCount]; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // ASYNCWATCHMODEL_H diff --git a/src/plugins/debugger/cdb/cdb.pri b/src/plugins/debugger/cdb/cdb.pri index 06f14409d32..3c7398567b8 100644 --- a/src/plugins/debugger/cdb/cdb.pri +++ b/src/plugins/debugger/cdb/cdb.pri @@ -39,7 +39,6 @@ HEADERS += \ $$PWD/cdbsymbolgroupcontext.h \ $$PWD/cdbsymbolgroupcontext_tpl.h \ $$PWD/cdbstacktracecontext.h \ - $$PWD/cdbstackframecontext.h \ $$PWD/cdbbreakpoint.h \ $$PWD/cdbmodules.h \ $$PWD/cdbassembler.h \ @@ -47,14 +46,14 @@ HEADERS += \ $$PWD/cdboptionspage.h \ $$PWD/cdbdumperhelper.h \ $$PWD/cdbsymbolpathlisteditor.h \ - $$PWD/cdbexceptionutils.h + $$PWD/cdbexceptionutils.h \ + $$PWD/cdbwatchmodels.h SOURCES += \ $$PWD/cdbdebugengine.cpp \ $$PWD/cdbdebugeventcallback.cpp \ $$PWD/cdbdebugoutput.cpp \ $$PWD/cdbsymbolgroupcontext.cpp \ - $$PWD/cdbstackframecontext.cpp \ $$PWD/cdbstacktracecontext.cpp \ $$PWD/cdbbreakpoint.cpp \ $$PWD/cdbmodules.cpp \ @@ -63,7 +62,8 @@ SOURCES += \ $$PWD/cdboptionspage.cpp \ $$PWD/cdbdumperhelper.cpp \ $$PWD/cdbsymbolpathlisteditor.cpp \ - $$PWD/cdbexceptionutils.cpp + $$PWD/cdbexceptionutils.cpp \ + $$PWD/cdbwatchmodels.cpp FORMS += $$PWD/cdboptionspagewidget.ui diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index 798f5234ff4..867cfb5a91c 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -30,7 +30,6 @@ #include "cdbdebugengine.h" #include "cdbdebugengine_p.h" #include "cdbstacktracecontext.h" -#include "cdbstackframecontext.h" #include "cdbsymbolgroupcontext.h" #include "cdbbreakpoint.h" #include "cdbmodules.h" @@ -47,6 +46,7 @@ #include "moduleshandler.h" #include "disassemblerhandler.h" #include "watchutils.h" +#include "cdbwatchmodels.h" #include <coreplugin/icore.h> #include <utils/qtcassert.h> @@ -301,7 +301,9 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent, m_debuggerManagerAccess(parent->engineInterface()), m_currentStackTrace(0), m_firstActivatedFrame(true), - m_mode(AttachCore) + m_mode(AttachCore), + m_localsModel(0), + m_watchModel(0) { } @@ -390,6 +392,15 @@ CdbDebugEnginePrivate::~CdbDebugEnginePrivate() m_cif.debugDataSpaces->Release(); } +void CdbDebugEnginePrivate::saveLocalsViewState() +{ + if (m_localsModel && m_localsModel->symbolGroupContext()) { + const int oldFrame = m_debuggerManagerAccess->stackHandler()->currentIndex(); + if (oldFrame != -1) + m_debuggerManagerAccess->watchHandler()->saveLocalsViewState(oldFrame); + } +} + void CdbDebugEnginePrivate::clearForRun() { if (debugCDB) @@ -403,6 +414,10 @@ void CdbDebugEnginePrivate::clearForRun() void CdbDebugEnginePrivate::cleanStackTrace() { + if (m_localsModel) { + saveLocalsViewState(); + m_localsModel->setSymbolGroupContext(0); + } if (m_currentStackTrace) { delete m_currentStackTrace; m_currentStackTrace = 0; @@ -465,14 +480,19 @@ QString CdbDebugEngine::editorToolTip(const QString &exp, const QString &functio QString errorMessage; QString rc; // Find the frame of the function if there is any - CdbStackFrameContext *frame = 0; + const QString iname = QLatin1String("local.") + exp; if (m_d->m_currentStackTrace && !function.isEmpty()) { const int frameIndex = m_d->m_currentStackTrace->indexOf(function); - if (frameIndex != -1) - frame = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage); + if (frameIndex != -1) { + // Are we at the current frame? + if (frameIndex == m_d->m_debuggerManagerAccess->stackHandler()->currentIndex()) { + if (const WatchData *wd = m_d->m_localsModel->findItemByIName(iname, m_d->m_localsModel->root())) + return wd->toToolTip(); } + // Nope, try to retrieve via another symbol group + if (m_d->m_currentStackTrace->editorToolTip(frameIndex, iname, &rc, &errorMessage)) + return rc; + } } - if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage)) - return rc; // No function/symbol context found, try to evaluate in current context. // Do not append type as this will mostly be 'long long' for integers, etc. QString type; @@ -770,67 +790,6 @@ void CdbDebugEngine::detachDebugger() m_d->endDebugging(CdbDebugEnginePrivate::EndDebuggingDetach); } -CdbStackFrameContext *CdbDebugEnginePrivate::getStackFrameContext(int frameIndex, QString *errorMessage) const -{ - if (!m_currentStackTrace) { - *errorMessage = QLatin1String(msgNoStackTraceC); - return 0; - } - if (CdbStackFrameContext *sg = m_currentStackTrace->frameContextAt(frameIndex, errorMessage)) - return sg; - return 0; -} - -void CdbDebugEngine::evaluateWatcher(WatchData *wd) -{ - if (debugCDBWatchHandling) - qDebug() << Q_FUNC_INFO << wd->exp; - QString errorMessage; - QString value; - QString type; - if (evaluateExpression(wd->exp, &value, &type, &errorMessage)) { - wd->setValue(value); - wd->setType(type); - } else { - wd->setValue(errorMessage); - wd->setTypeUnneeded(); - } - wd->setHasChildren(false); -} - -void CdbDebugEngine::updateWatchData(const WatchData &incomplete) -{ - // Watch item was edited while running - if (m_d->isDebuggeeRunning()) - return; - - if (debugCDBWatchHandling) - qDebug() << Q_FUNC_INFO << "\n " << incomplete.toString(); - - WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler(); - if (incomplete.iname.startsWith(QLatin1String("watch."))) { - WatchData watchData = incomplete; - evaluateWatcher(&watchData); - watchHandler->insertData(watchData); - return; - } - - const int frameIndex = m_d->m_debuggerManagerAccess->stackHandler()->currentIndex(); - - bool success = false; - QString errorMessage; - do { - CdbStackFrameContext *sg = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage); - if (!sg) - break; - if (!sg->completeData(incomplete, watchHandler, &errorMessage)) - break; - success = true; - } while (false); - if (!success) - warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage)); -} - void CdbDebugEngine::stepExec() { if (debugCDB) @@ -1037,18 +996,14 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v bool success = false; do { QString newValue; - CdbStackFrameContext *sg = m_d->getStackFrameContext(frameIndex, &errorMessage); - if (!sg) - break; - if (!sg->assignValue(expr, value, &newValue, &errorMessage)) + if (frameIndex < 0 || !m_d->m_currentStackTrace) { + errorMessage = tr("No current stack trace."); break; - // Update view - WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler(); - if (WatchData *fwd = watchHandler->findItem(expr)) { - fwd->setValue(newValue); - watchHandler->insertData(*fwd); - watchHandler->updateWatchers(); } + // Assign in stack and update view + if (!m_d->m_currentStackTrace->assignValue(frameIndex, expr, value, &newValue, &errorMessage)) + break; + m_d->m_localsModel->setValueByExpression(expr, newValue); success = true; } while (false); if (!success) { @@ -1078,6 +1033,11 @@ bool CdbDebugEnginePrivate::executeDebuggerCommand(CIDebugControl *ctrl, const Q return true; } +bool CdbDebugEngine::isDebuggeeHalted() const +{ + return m_d->m_hDebuggeeProcess && !m_d->isDebuggeeRunning(); +} + bool CdbDebugEngine::evaluateExpression(const QString &expression, QString *value, QString *type, @@ -1132,21 +1092,21 @@ void CdbDebugEngine::activateFrame(int frameIndex) bool success = false; do { StackHandler *stackHandler = m_d->m_debuggerManagerAccess->stackHandler(); - WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler(); const int oldIndex = stackHandler->currentIndex(); if (frameIndex >= stackHandler->stackSize()) { errorMessage = msgStackIndexOutOfRange(frameIndex, stackHandler->stackSize()); break; } - if (oldIndex != frameIndex) + if (oldIndex != frameIndex) { + m_d->saveLocalsViewState(); stackHandler->setCurrentIndex(frameIndex); + } const StackFrame &frame = stackHandler->currentFrame(); if (!frame.isUsable()) { + m_d->m_localsModel->setSymbolGroupContext(0); // Clean out model - watchHandler->beginCycle(); - watchHandler->endCycle(); errorMessage = QString::fromLatin1("%1: file %2 unusable."). arg(QLatin1String(Q_FUNC_INFO), frame.file); break; @@ -1155,10 +1115,18 @@ void CdbDebugEngine::activateFrame(int frameIndex) m_d->m_debuggerManager->gotoLocation(frame.file, frame.line, true); if (oldIndex != frameIndex || m_d->m_firstActivatedFrame) { - watchHandler->beginCycle(); - if (CdbStackFrameContext *sgc = m_d->getStackFrameContext(frameIndex, &errorMessage)) - success = sgc->populateModelInitially(watchHandler, &errorMessage); - watchHandler->endCycle(); + m_d->m_localsModel->setUseDumpers(m_d->m_dumper->isEnabled() && theDebuggerBoolSetting(UseDebuggingHelpers)); + CdbSymbolGroupContext *ctx = 0; + if (m_d->m_currentStackTrace) { + ctx = m_d->m_currentStackTrace->symbolGroupAt(frameIndex, &errorMessage); + success = ctx != 0; + } else { + errorMessage = QLatin1String(msgNoStackTraceC); + } + m_d->m_localsModel->setSymbolGroupContext(ctx); + m_d->m_debuggerManagerAccess->watchHandler()->restoreLocalsViewState(frameIndex); + } else { + success = true; } } while (false); if (!success) @@ -1222,7 +1190,7 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa m_cif.debugSymbols, m_debuggerManagerAccess->breakHandler(), errorMessage, &warnings); - if (const int warningsCount = warnings.size()) + if (const int warningsCount = warnings.size()) for (int w = 0; w < warningsCount; w++) m_engine->warning(warnings.at(w)); return ok; @@ -1500,7 +1468,8 @@ void CdbDebugEnginePrivate::updateStackTrace() m_debuggerManagerAccess->stackHandler()->setCurrentIndex(current); m_engine->activateFrame(current); } - m_debuggerManagerAccess->watchHandler()->updateWatchers(); + // Update watchers + m_watchModel->refresh(); } void CdbDebugEnginePrivate::updateModules() @@ -1512,8 +1481,6 @@ void CdbDebugEnginePrivate::updateModules() m_debuggerManagerAccess->modulesHandler()->setModules(modules); } -static const char *dumperPrefixC = "dumper"; - void CdbDebugEnginePrivate::handleModuleLoad(const QString &name) { if (debugCDB>2) @@ -1586,6 +1553,37 @@ bool CdbDebugEnginePrivate::setSymbolPaths(const QStringList &s, QString *errorM return true; } +void CdbDebugEngine::insertWatcher(const WatchData &wd) +{ + // Make sure engine is active + if (m_d->m_watchModel && m_d->m_watchModel == m_d->m_debuggerManagerAccess->watchHandler()->model(WatchersWatch)) + m_d->m_watchModel->addWatcher(wd); +} + +WatchModel *CdbDebugEngine::watchModel(int type) const +{ + switch (type) { + case LocalsWatch: + if (!m_d->m_localsModel) { + m_d->m_localsModel = new CdbLocalsModel(m_d->m_dumper, m_d->m_debuggerManagerAccess->watchHandler(), LocalsWatch, const_cast<CdbDebugEngine*>(this)); + connect(m_d->m_localsModel, SIGNAL(error(QString)), this, SLOT(warning(QString))); + } + return m_d->m_localsModel; + case WatchersWatch: + if (!m_d->m_watchModel) { + CdbDebugEngine* nonConstThis = const_cast<CdbDebugEngine*>(this); + WatchHandler *wh = m_d->m_debuggerManagerAccess->watchHandler(); + m_d->m_watchModel = new CdbWatchModel(nonConstThis, m_d->m_dumper, wh, WatchersWatch, nonConstThis); + connect(m_d->m_watchModel, SIGNAL(error(QString)), this, SLOT(warning(QString))); + connect(wh, SIGNAL(watcherInserted(WatchData)), this, SLOT(insertWatcher(WatchData))); + } + return m_d->m_watchModel; + default: + break; + } + return 0; +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbdebugengine.h b/src/plugins/debugger/cdb/cdbdebugengine.h index 13725bb97e4..8c827769709 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.h +++ b/src/plugins/debugger/cdb/cdbdebugengine.h @@ -64,15 +64,14 @@ public: virtual bool startDebugger(const QSharedPointer<DebuggerStartParameters> &startParameters); virtual void exitDebugger(); virtual void detachDebugger(); - virtual void updateWatchData(const WatchData &data); virtual void stepExec(); virtual void stepOutExec(); virtual void nextExec(); virtual void stepIExec(); virtual void nextIExec(); - - virtual void continueInferior(); + + virtual void continueInferior(); virtual void interruptInferior(); virtual void runToLineExec(const QString &fileName, int lineNumber); @@ -97,6 +96,11 @@ public: virtual void reloadSourceFiles(); virtual void reloadFullStack() {} + WatchModel *watchModel(int type) const; + + bool isDebuggeeHalted() const; + bool evaluateExpression(const QString &expression, QString *value, QString *type, QString *errorMessage); + public slots: void syncDebuggerPaths(); @@ -106,8 +110,9 @@ protected: private slots: void slotConsoleStubStarted(); void slotConsoleStubError(const QString &msg); - void slotConsoleStubTerminated(); + void slotConsoleStubTerminated(); void warning(const QString &w); + void insertWatcher(const WatchData &); private: bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage); @@ -116,8 +121,7 @@ private: void killWatchTimer(); void processTerminated(unsigned long exitCode); bool executeDebuggerCommand(const QString &command, QString *errorMessage); - bool evaluateExpression(const QString &expression, QString *value, QString *type, QString *errorMessage); - void evaluateWatcher(WatchData *wd); + QString editorToolTip(const QString &exp, const QString &function); CdbDebugEnginePrivate *m_d; diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h index 23eb5648275..910486aa8b5 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine_p.h +++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h @@ -39,6 +39,7 @@ #include <utils/consoleprocess.h> #include <QtCore/QSharedPointer> +#include <QtCore/QPointer> #include <QtCore/QMap> namespace Debugger { @@ -49,6 +50,8 @@ class IDebuggerManagerAccessForEngines; class WatchHandler; class CdbStackFrameContext; class CdbStackTraceContext; +class CdbLocalsModel; +class CdbWatchModel; // Thin wrapper around the 'DBEng' debugger engine shared library // which is loaded at runtime. @@ -115,7 +118,7 @@ struct CdbDebugEnginePrivate bool isDebuggeeRunning() const { return m_watchTimer != -1; } void handleDebugEvent(); - void updateThreadList(); + void updateThreadList(); void updateStackTrace(); void updateModules(); @@ -123,7 +126,6 @@ struct CdbDebugEnginePrivate void cleanStackTrace(); void clearForRun(); void handleModuleLoad(const QString &); - CdbStackFrameContext *getStackFrameContext(int frameIndex, QString *errorMessage) const; void clearDisplay(); bool interruptInterferiorProcess(QString *errorMessage); @@ -137,6 +139,8 @@ struct CdbDebugEnginePrivate enum EndDebuggingMode { EndDebuggingDetach, EndDebuggingTerminate, EndDebuggingAuto }; void endDebugging(EndDebuggingMode em = EndDebuggingAuto); + void saveLocalsViewState(); + static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage); static bool evaluateExpression(CIDebugControl *ctrl, const QString &expression, DEBUG_VALUE *v, QString *errorMessage); @@ -155,7 +159,7 @@ struct CdbDebugEnginePrivate int m_watchTimer; CdbComInterfaces m_cif; CdbDebugEventCallback m_debugEventCallBack; - CdbDebugOutput m_debugOutputCallBack; + CdbDebugOutput m_debugOutputCallBack; QSharedPointer<CdbDumperHelper> m_dumper; CdbDebugEngine* m_engine; @@ -168,6 +172,9 @@ struct CdbDebugEnginePrivate DebuggerStartMode m_mode; Core::Utils::ConsoleProcess m_consoleStubProc; + + QPointer<CdbLocalsModel> m_localsModel; + QPointer<CdbWatchModel> m_watchModel; }; // helper functions diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp index 7663f315d0a..d3d927cd49f 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp @@ -28,11 +28,11 @@ **************************************************************************/ #include "cdbstacktracecontext.h" -#include "cdbstackframecontext.h" #include "cdbbreakpoint.h" #include "cdbsymbolgroupcontext.h" #include "cdbdebugengine_p.h" #include "cdbdumperhelper.h" +#include "debuggeractions.h" #include <QtCore/QDir> #include <QtCore/QTextStream> @@ -90,7 +90,7 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa qDebug() << Q_FUNC_INFO << frameCount; m_frameContexts.resize(frameCount); - qFill(m_frameContexts, static_cast<CdbStackFrameContext*>(0)); + qFill(m_frameContexts, static_cast<CdbSymbolGroupContext*>(0)); // Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames WCHAR wszBuf[MAX_PATH]; @@ -145,7 +145,7 @@ static inline QString msgFrameContextFailed(int index, const StackFrame &f, cons arg(index).arg(f.function).arg(f.line).arg(f.file, why); } -CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *errorMessage) +CdbSymbolGroupContext *CdbStackTraceContext::symbolGroupAt(int index, QString *errorMessage) { // Create a frame on demand if (debugCDB) @@ -158,6 +158,7 @@ CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *e } if (m_frameContexts.at(index)) return m_frameContexts.at(index); + // Create COM and wrap CIDebugSymbolGroup *sg = createSymbolGroup(index, errorMessage); if (!sg) { *errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage); @@ -168,9 +169,8 @@ CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *e *errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage); return 0; } - CdbStackFrameContext *fr = new CdbStackFrameContext(m_dumper, sc); - m_frameContexts[index] = fr; - return fr; + m_frameContexts[index] = sc; + return sc; } CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage) @@ -198,6 +198,49 @@ CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString * return sg; } +bool CdbStackTraceContext::editorToolTip(int frameIndex, + const QString &iname, + QString *value, + QString *errorMessage) +{ + value->clear(); + // Look up iname in the frame's symbol group. + CdbSymbolGroupContext *m_symbolContext = symbolGroupAt(frameIndex, errorMessage); + if (!m_symbolContext) + return false; + unsigned long index; + if (!m_symbolContext->lookupPrefix(iname, &index)) { + *errorMessage = QString::fromLatin1("%1 not found.").arg(iname); + return false; + } + const WatchData wd = m_symbolContext->symbolAt(index); + // Check dumpers. Should actually be just one item. + if (theDebuggerBoolSetting(UseDebuggingHelpers) && m_dumper->isEnabled()) { + QList<WatchData> result; + if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, 1, &result, errorMessage)) { + foreach (const WatchData &dwd, result) { + if (!value->isEmpty()) + value->append(QLatin1Char('\n')); + value->append(dwd.toToolTip()); + } + return true; + } // Dumped ok + } // has Dumpers + *value = wd.toToolTip(); + return true; +} + +bool CdbStackTraceContext::assignValue(int frameIndex, + const QString &iname, + const QString &value, + QString *newValue /* = 0 */, QString *errorMessage) +{ + if (CdbSymbolGroupContext *symbolContext = symbolGroupAt(frameIndex, errorMessage)) + return symbolContext->assignValue(iname, value, newValue, errorMessage); + return false; +} + + QString CdbStackTraceContext::toString() const { QString rc; diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.h b/src/plugins/debugger/cdb/cdbstacktracecontext.h index 9dfbdf04745..d878472450b 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.h +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.h @@ -47,11 +47,10 @@ namespace Internal { struct CdbComInterfaces; class CdbSymbolGroupContext; -class CdbStackFrameContext; class CdbDumperHelper; /* Context representing a break point stack consisting of several frames. - * Maintains an on-demand constructed list of CdbStackFrameContext + * Maintains an on-demand constructed list of CdbSymbolGroupContext * containining the local variables of the stack. */ class CdbStackTraceContext @@ -75,7 +74,17 @@ public: // Top-Level instruction offset for disassembler ULONG64 instructionOffset() const { return m_instructionOffset; } - CdbStackFrameContext *frameContextAt(int index, QString *errorMessage); + CdbSymbolGroupContext *symbolGroupAt(int index, QString *errorMessage); + + // Helper to retrieve an editor tooltip for a frame. Note that + // for the current frame, the LocalsModel should be consulted first. + bool editorToolTip(int frameIndex,const QString &iname, QString *value, QString *errorMessage); + + // Assign value in Debugger + bool assignValue(int frameIndex, + const QString &iname, + const QString &value, + QString *newValue /* = 0 */, QString *errorMessage); // Format for logging void format(QTextStream &str) const; @@ -89,7 +98,7 @@ private: CdbComInterfaces *m_cif; DEBUG_STACK_FRAME m_cdbFrames[maxFrames]; - QVector <CdbStackFrameContext*> m_frameContexts; + QVector <CdbSymbolGroupContext*> m_frameContexts; QList<StackFrame> m_frames; ULONG64 m_instructionOffset; }; diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp index 5d82835f53d..94bf9814f62 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp @@ -406,7 +406,6 @@ WatchData CdbSymbolGroupContext::symbolAt(unsigned long index) const const QString value = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, index); wd.setType(type); wd.setValue(fixValue(value)); - wd.setChildrenNeeded(); // compensate side effects of above setters // Figure out children. The SubElement is only a guess unless the symbol, // is expanded, so, we leave this as a guess for later updates. // If the symbol has children (expanded or not), we leave the 'Children' flag diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h index 91ec5fd9240..16b7f5fab61 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h @@ -81,31 +81,6 @@ public: bool assignValue(const QString &iname, const QString &value, QString *newValue /* = 0 */, QString *errorMessage); - // Initially populate the locals model for a new stackframe. - // Write a sequence of WatchData to it, recurse down if the - // recursionPredicate agrees. The ignorePredicate can be used - // to terminate processing after insertion of an item (if the calling - // routine wants to insert another subtree). - template <class OutputIterator, class RecursionPredicate, class IgnorePredicate> - static bool populateModelInitially(CdbSymbolGroupContext *sg, - OutputIterator it, - RecursionPredicate recursionPredicate, - IgnorePredicate ignorePredicate, - QString *errorMessage); - - // Complete children of a WatchData item. - // Write a sequence of WatchData to it, recurse if the - // recursionPredicate agrees. The ignorePredicate can be used - // to terminate processing after insertion of an item (if the calling - // routine wants to insert another subtree). - template <class OutputIterator, class RecursionPredicate, class IgnorePredicate> - static bool completeData (CdbSymbolGroupContext *sg, - WatchData incompleteLocal, - OutputIterator it, - RecursionPredicate recursionPredicate, - IgnorePredicate ignorePredicate, - QString *errorMessage); - // Retrieve child symbols of prefix as a sequence of WatchData. template <class OutputIterator> bool getChildSymbols(const QString &prefix, OutputIterator it, QString *errorMessage); diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h b/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h index ccaba97ee89..5aa9c35a2fd 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h @@ -66,105 +66,6 @@ bool CdbSymbolGroupContext::getChildSymbols(const QString &prefix, OutputIterato return true; } -// Insert a symbol (and its first level children depending on forceRecursion) -// The parent symbol is inserted before the children to make dumper handling -// simpler. In theory, it can happen that the symbol context indicates -// children but can expand none, which would lead to invalid parent information -// (expand icon), though (ignore for simplicity). -template <class OutputIterator, class RecursionPredicate, class IgnorePredicate> -bool insertSymbolRecursion(WatchData wd, - CdbSymbolGroupContext *sg, - OutputIterator it, - RecursionPredicate recursionPredicate, - IgnorePredicate ignorePredicate, - QString *errorMessage) -{ - // Find out whether to recurse (has children and predicate agrees) - const bool hasChildren = wd.hasChildren || wd.isChildrenNeeded(); - const bool recurse = hasChildren && recursionPredicate(wd); - if (debugSgRecursion) - qDebug() << "insertSymbolRecursion" << '\n' << wd.iname << "recurse=" << recurse; - if (!recurse) { - // No further recursion at this level, pretend entry is complete - // to the watchmodel for the parent to show (only remaining watchhandler-specific - // part here). - if (wd.isChildrenNeeded()) { - wd.setHasChildren(true); - wd.setChildrenUnneeded(); - } - if (debugSgRecursion) - qDebug() << " INSERTING non-recursive: " << wd.toString(); - *it = wd; - ++it; - return true; - } - // Recursion: Indicate children unneeded - wd.setHasChildren(true); - wd.setChildrenUnneeded(); - if (debugSgRecursion) - qDebug() << " INSERTING recursive: " << wd.toString(); - *it = wd; - ++it; - // Recurse unless the predicate disagrees - if (ignorePredicate(wd)) - return true; - QList<WatchData> watchList; - // This implicitly enforces expansion - if (!sg->getChildSymbols(wd.iname, WatchDataBackInserter(watchList), errorMessage)) - return false; - const int childCount = watchList.size(); - for (int c = 0; c < childCount; c++) { - const WatchData &cwd = watchList.at(c); - if (wd.isValid()) { // We sometimes get empty names for deeply nested data - if (!insertSymbolRecursion(cwd, sg, it, recursionPredicate, ignorePredicate, errorMessage)) - return false; - } else { - const QString msg = QString::fromLatin1("WARNING: Skipping invalid child symbol #%2 (type %3) of '%4'."). - arg(QLatin1String(Q_FUNC_INFO)).arg(c).arg(cwd.type, wd.iname); - qWarning("%s\n", qPrintable(msg)); - } - } - return true; -} - -template <class OutputIterator, class RecursionPredicate, class IgnorePredicate> -bool CdbSymbolGroupContext::populateModelInitially(CdbSymbolGroupContext *sg, - OutputIterator it, - RecursionPredicate recursionPredicate, - IgnorePredicate ignorePredicate, - QString *errorMessage) -{ - if (debugSgRecursion) - qDebug() << "### CdbSymbolGroupContext::populateModelInitially"; - - // Insert root items - QList<WatchData> watchList; - if (!sg->getChildSymbols(sg->prefix(), WatchDataBackInserter(watchList), errorMessage)) - return false; - // Insert data - foreach(const WatchData &wd, watchList) - if (!insertSymbolRecursion(wd, sg, it, recursionPredicate, ignorePredicate, errorMessage)) - return false; - return true; -} - -template <class OutputIterator, class RecursionPredicate, class IgnorePredicate> -bool CdbSymbolGroupContext::completeData(CdbSymbolGroupContext *sg, - WatchData incompleteLocal, - OutputIterator it, - RecursionPredicate recursionPredicate, - IgnorePredicate ignorePredicate, - QString *errorMessage) -{ - if (debugSgRecursion) - qDebug().nospace() << "###>CdbSymbolGroupContext::completeData" << ' ' << incompleteLocal.iname << '\n'; - // If the symbols are already expanded in the context, they will be re-inserted, - // which is not handled for simplicity. - if (!insertSymbolRecursion(incompleteLocal, sg, it, recursionPredicate, ignorePredicate, errorMessage)) - return false; - return true; -} - } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbwatchmodels.cpp b/src/plugins/debugger/cdb/cdbwatchmodels.cpp new file mode 100644 index 00000000000..0f5bb70846f --- /dev/null +++ b/src/plugins/debugger/cdb/cdbwatchmodels.cpp @@ -0,0 +1,477 @@ +/************************************************************************** +** +** 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 "cdbwatchmodels.h" +#include "cdbdumperhelper.h" +#include "cdbsymbolgroupcontext.h" +#include "cdbdebugengine.h" +#include "watchutils.h" +#include "debuggeractions.h" + +#include <QtCore/QDebug> +#include <QtCore/QList> +#include <QtCore/QCoreApplication> +#include <QtCore/QRegExp> + +enum { debugCDBWatchHandling = 0 }; + +namespace Debugger { +namespace Internal { + +enum { LocalsOwnerSymbolGroup, LocalsOwnerDumper }; + +static inline QString msgNoStackFrame() +{ + return QCoreApplication::translate("CdbWatchModels", "No active stack frame present."); +} + +static inline QString msgUnknown() +{ + return QCoreApplication::translate("CdbWatchModels", "<Unknown>"); +} + +// Helper to add a sequence of WatchData from the symbol group context to an item +// without using dumpers. + +class SymbolGroupInserter +{ +public: + explicit SymbolGroupInserter(WatchItem *parent) : m_parent(parent) {} + + inline SymbolGroupInserter& operator*() { return *this; } + inline SymbolGroupInserter&operator=(const WatchData &wd) { + WatchItem *item = new WatchItem(wd); + item->source = LocalsOwnerSymbolGroup; + m_parent->addChild(item); + return *this; + } + inline SymbolGroupInserter&operator++() { return *this; } + +private: + WatchItem *m_parent; +}; + +// fixDumperResult: When querying an item, the queried item is sometimes returned in +// incomplete form. Take over values from source, set items with missing addresses to +// "complete". +static inline void fixDumperResult(const WatchData &source, + QList<WatchData> *result) +{ + if (debugCDBWatchHandling > 1) { + qDebug() << "fixDumperResult for : " << source.toString(); + foreach (const WatchData &wd, *result) + qDebug() << " " << wd.toString(); + } + const int size = result->size(); + if (!size) + return; + WatchData &returned = result->front(); + if (returned.iname != source.iname) + return; + if (returned.type.isEmpty()) + returned.setType(source.type); + if (returned.isValueNeeded()) { + if (source.isValueKnown()) { + returned.setValue(source.value); + } else { + // Should not happen + returned.setValue(msgUnknown()); + qWarning("%s: No value for %s\n", Q_FUNC_INFO, qPrintable(returned.toString())); + } + } + if (size == 1) + return; + // Fix the children: If the address is missing, we cannot query any further. + const QList<WatchData>::iterator wend = result->end(); + QList<WatchData>::iterator it = result->begin(); + for (++it; it != wend; ++it) { + WatchData &wd = *it; + if (wd.addr.isEmpty() && wd.isSomethingNeeded()) { + wd.setAllUnneeded(); + } + } +} + +// Dump an item. If *ptrToDumpedItem == 0, allocate a new item and set it. +// If it is non-null, the item pointed to will receive the results +// ("complete" functionality). +static CdbDumperHelper::DumpResult + dumpItem(const QSharedPointer<CdbDumperHelper> dumper, + const WatchData &wd, + WatchItem **ptrToDumpedItem, + int dumperOwnerValue, QString *errorMessage) +{ + QList<WatchData> dumperResult; + WatchItem *dumpedItem = *ptrToDumpedItem; + const CdbDumperHelper::DumpResult rc = dumper->dumpType(wd, true, dumperOwnerValue, &dumperResult, errorMessage); + if (debugCDBWatchHandling > 1) + qDebug() << "dumper for " << wd.type << " returns " << rc; + + switch (rc) { + case CdbDumperHelper::DumpError: + return rc; + case CdbDumperHelper::DumpNotHandled: + errorMessage->clear(); + return rc; + case CdbDumperHelper::DumpOk: + break; + } + // Dumpers omit types for complicated templates + fixDumperResult(wd, &dumperResult); + // Discard the original item and insert the dumper results + if (dumpedItem) { + dumpedItem->setData(dumperResult.front()); + } else { + dumpedItem = new WatchItem(dumperResult.front()); + *ptrToDumpedItem = dumpedItem; + } + dumperResult.pop_front(); + foreach(const WatchData &dwd, dumperResult) + dumpedItem->addChild(new WatchItem(dwd)); + return rc; +} + +// Is this a non-null pointer to a dumpeable item with a value +// "0x4343 class QString *" ? - Insert a fake '*' dereferenced item +// and run dumpers on it. If that succeeds, insert the fake items owned by dumpers, +// Note that the symbol context does not create '*' dereferenced items for +// classes (see note in its header documentation). +static bool expandPointerToDumpable(const QSharedPointer<CdbDumperHelper> dumper, + const WatchData &wd, + WatchItem *parent, + QString *errorMessage) +{ + + + if (debugCDBWatchHandling > 1) + qDebug() << ">expandPointerToDumpable" << wd.iname; + + WatchItem *derefedWdItem = 0; + WatchItem *ptrWd = 0; + bool handled = false; + do { + if (!isPointerType(wd.type)) + break; + const int classPos = wd.value.indexOf(" class "); + if (classPos == -1) + break; + const QString hexAddrS = wd.value.mid(0, classPos); + static const QRegExp hexNullPattern(QLatin1String("0x0+")); + Q_ASSERT(hexNullPattern.isValid()); + if (hexNullPattern.exactMatch(hexAddrS)) + break; + const QString type = stripPointerType(wd.value.mid(classPos + 7)); + WatchData derefedWd; + derefedWd.setType(type); + derefedWd.setAddress(hexAddrS); + derefedWd.name = QString(QLatin1Char('*')); + derefedWd.iname = wd.iname + QLatin1String(".*"); + derefedWd.source = LocalsOwnerDumper; + QList<WatchData> dumperResult; + const CdbDumperHelper::DumpResult dr = dumpItem(dumper, derefedWd, &derefedWdItem, LocalsOwnerDumper, errorMessage); + if (dr != CdbDumperHelper::DumpOk) + break; + // Insert the pointer item with 1 additional child + its dumper results + // Note: formal arguments might already be expanded in the symbol group. + ptrWd = new WatchItem(wd); + ptrWd->source = LocalsOwnerDumper; + ptrWd->setHasChildren(true); + ptrWd->setChildrenUnneeded(); + ptrWd->addChild(derefedWdItem); + parent->addChild(ptrWd); + handled = true; + } while (false); + if (!handled) { + delete derefedWdItem; + delete ptrWd; + } + if (debugCDBWatchHandling > 1) + qDebug() << "<expandPointerToDumpable returns " << handled << *errorMessage; + return handled; +} + +// Main routine for inserting an item from the symbol group using the dumpers +// where applicable. +static inline bool insertDumpedItem(const QSharedPointer<CdbDumperHelper> &dumper, + const WatchData &wd, + WatchItem *parent, + QString *errorMessage) +{ + if (debugCDBWatchHandling > 1) + qDebug() << "insertItem=" << wd.toString(); + // Check pointer to dumpeable, dumpeable, insert accordingly. + if (expandPointerToDumpable(dumper, wd, parent, errorMessage)) + return true; + WatchItem *dumpedItem = 0; + // Add item owned by Dumper or symbol group on failure. + const CdbDumperHelper::DumpResult dr = dumpItem(dumper, wd, &dumpedItem, LocalsOwnerDumper, errorMessage); + switch (dr) { + case CdbDumperHelper::DumpOk: + dumpedItem->parent = parent; + parent->children.push_back(dumpedItem); + break; + case CdbDumperHelper::DumpNotHandled: + case CdbDumperHelper::DumpError: { + WatchItem *symbolItem = new WatchItem(wd); + symbolItem->source = LocalsOwnerSymbolGroup; + parent->addChild(symbolItem); + } + break; + } + return true; +} + +// Helper to add a sequence of WatchData from the symbol group context to an item. +// Checks if the item is dumpable in some way; if so, dump it and use that instead of +// symbol group. +class SymbolGroupDumperInserter +{ +public: + explicit SymbolGroupDumperInserter(const QSharedPointer<CdbDumperHelper> &dumper, + WatchItem *parent, + QString *errorMessage); + + inline SymbolGroupDumperInserter& operator*() { return *this; } + inline SymbolGroupDumperInserter&operator=(const WatchData &wd) { + insertDumpedItem(m_dumper, wd, m_parent, m_errorMessage); + return *this; + } + inline SymbolGroupDumperInserter&operator++() { return *this; } + +private: + const QSharedPointer<CdbDumperHelper> m_dumper; + WatchItem *m_parent; + QString *m_errorMessage; +}; + +SymbolGroupDumperInserter::SymbolGroupDumperInserter(const QSharedPointer<CdbDumperHelper> &dumper, + WatchItem *parent, + QString *errorMessage) : + m_dumper(dumper), + m_parent(parent), + m_errorMessage(errorMessage) +{ +} + +// -------------- CdbLocalsModel + +CdbLocalsModel::CdbLocalsModel(const QSharedPointer<CdbDumperHelper> &dh, + WatchHandler *handler, WatchType type, QObject *parent) : + AbstractSyncWatchModel(handler, type, parent), + m_dumperHelper(dh), + m_symbolGroupContext(0), + m_useDumpers(false) +{ +} + +CdbLocalsModel::~CdbLocalsModel() +{ +} + +bool CdbLocalsModel::fetchChildren(WatchItem *wd, QString *errorMessage) +{ + if (!m_symbolGroupContext) { + *errorMessage = msgNoStackFrame(); + return false; + } + if (debugCDBWatchHandling) + qDebug() << "fetchChildren" << wd->iname; + + // Check the owner and call it to expand the item. + switch (wd->source) { + case LocalsOwnerSymbolGroup: + if (m_useDumpers && m_dumperHelper->isEnabled()) { + SymbolGroupDumperInserter inserter(m_dumperHelper, wd, errorMessage); + return m_symbolGroupContext->getChildSymbols(wd->iname, inserter, errorMessage); + } else { + return m_symbolGroupContext->getChildSymbols(wd->iname, SymbolGroupInserter(wd), errorMessage); + } + break; + case LocalsOwnerDumper: + if (dumpItem(m_dumperHelper, *wd, &wd, LocalsOwnerDumper, errorMessage) == CdbDumperHelper::DumpOk) + return true; + if (wd->isValueNeeded()) + wd->setValue(msgUnknown()); + qWarning("%s: No value for %s\n", Q_FUNC_INFO, qPrintable(wd->toString())); + return false; + } + return false; +} + +bool CdbLocalsModel::complete(WatchItem *wd, QString *errorMessage) +{ + if (!m_symbolGroupContext) { + *errorMessage = msgNoStackFrame(); + return false; + } + if (debugCDBWatchHandling) + qDebug() << "complete" << wd->iname; + // Might as well fetch children when completing a dumped item. + return fetchChildren(wd, errorMessage); +} + +void CdbLocalsModel::setSymbolGroupContext(CdbSymbolGroupContext *s) +{ + if (s == m_symbolGroupContext) + return; + if (debugCDBWatchHandling) + qDebug() << ">setSymbolGroupContext" << s; + m_symbolGroupContext = s; + reinitialize(); + if (!m_symbolGroupContext) + return; + // Populate first row + WatchItem *item = dummyRoot(); + QString errorMessage; + do { + if (m_useDumpers && m_dumperHelper->isEnabled()) { + SymbolGroupDumperInserter inserter(m_dumperHelper, item, &errorMessage); + if (!m_symbolGroupContext->getChildSymbols(m_symbolGroupContext->prefix(), inserter, &errorMessage)) { + break; + } + } else { + if (!m_symbolGroupContext->getChildSymbols(m_symbolGroupContext->prefix(), SymbolGroupInserter(item), &errorMessage)) + break; + } + if (item->children.empty()) + break; + reset(); + } while (false); + if (!errorMessage.isEmpty()) + emit error(errorMessage); + if (debugCDBWatchHandling) + qDebug() << "<setSymbolGroupContext" << item->children.size() << errorMessage; + if (debugCDBWatchHandling > 1) + qDebug() << '\n' << *this; +} + +// ---- CdbWatchModel + +enum { WatchOwnerNewItem, WatchOwnerExpression, WatchOwnerDumper }; + +CdbWatchModel::CdbWatchModel(CdbDebugEngine *engine, + const QSharedPointer<CdbDumperHelper> &dh, + WatchHandler *handler, + WatchType type, QObject *parent) : + AbstractSyncWatchModel(handler, type, parent), + m_dumperHelper(dh), + m_engine(engine) +{ +} + +CdbWatchModel::~CdbWatchModel() +{ +} + +bool CdbWatchModel::evaluateWatchExpression(WatchData *wd, QString *errorMessage) +{ + QString value; + QString type; + + const bool rc = m_engine->evaluateExpression(wd->exp, &value, &type, errorMessage); + if (!rc) { + wd->setValue(msgUnknown()); + return false; + } + wd->setValue(value); + wd->setType(type); + return true; +} + +bool CdbWatchModel::fetchChildren(WatchItem *wd, QString *errorMessage) +{ + if (debugCDBWatchHandling) + qDebug() << "Watch:fetchChildren" << wd->iname << wd->source; + // We need to be halted. + if (!m_engine->isDebuggeeHalted()) { + *errorMessage = QCoreApplication::translate("CdbWatchModels", "Can evaluates watches only in halted state."); + wd->setValue(msgUnknown()); + return false; + } + // New item with address -> dumper. + if (wd->source == WatchOwnerNewItem) + wd->source = wd->addr.isEmpty() ? WatchOwnerExpression : WatchOwnerDumper; + // Expressions + if (wd->source == WatchOwnerExpression) + return evaluateWatchExpression(wd, errorMessage); + // Variables by address + if (!m_dumperHelper->isEnabled() || !theDebuggerBoolSetting(UseDebuggingHelpers)) { + *errorMessage = QCoreApplication::translate("CdbWatchModels", "Cannot evaluate '%1' due to dumpers being disabled.").arg(wd->name); + return false; + } + return dumpItem(m_dumperHelper, *wd, &wd, WatchOwnerDumper, errorMessage) == CdbDumperHelper::DumpOk; +} + +bool CdbWatchModel::complete(WatchItem *wd, QString *errorMessage) +{ + return fetchChildren(wd, errorMessage); +} + +void CdbWatchModel::addWatcher(const WatchData &wd) +{ + WatchItem *root = dummyRoot(); + if (!root) + return; + const QModelIndex rootIndex = watchIndex(root); + beginInsertRows(rootIndex, root->children.size(), root->children.size()); + root->addChild(new WatchItem(wd)); + endInsertRows(); +} + +void CdbWatchModel::refresh() +{ + // Refresh data for a new break + WatchItem *root = dummyRoot(); + if (!root) + return; + const int childCount = root->children.size(); + if (!childCount) + return; + // reset flags, if there children, trigger a reset + bool resetRequired = false; + for (int i = 0; i < childCount; i++) { + WatchItem *topLevel = root->children.at(i); + topLevel->setAllNeeded(); + if (!topLevel->children.empty()) { + topLevel->removeChildren(); + resetRequired = true; + } + } + // notify model + if (resetRequired) { + reset(); + } else { + const QModelIndex topLeft = watchIndex(root->children.front()); + const QModelIndex bottomLeft = root->children.size() == 1 ? topLeft : watchIndex(root->children.back()); + emit dataChanged(topLeft.sibling(0, 1), bottomLeft.sibling(0, columnCount() -1)); + } +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbwatchmodels.h b/src/plugins/debugger/cdb/cdbwatchmodels.h new file mode 100644 index 00000000000..29c8be80505 --- /dev/null +++ b/src/plugins/debugger/cdb/cdbwatchmodels.h @@ -0,0 +1,96 @@ +/************************************************************************** +** +** 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 CDBWATCHMODELS_H +#define CDBWATCHMODELS_H + +#include "abstractsyncwatchmodel.h" +#include <QtCore/QSharedPointer> + +namespace Debugger { +namespace Internal { + +class CdbSymbolGroupContext; +class CdbDumperHelper; +class CdbDebugEngine; + +class CdbLocalsModel : public AbstractSyncWatchModel { + Q_DISABLE_COPY(CdbLocalsModel) + Q_OBJECT +public: + explicit CdbLocalsModel(const QSharedPointer<CdbDumperHelper> &dh, + WatchHandler *handler, WatchType type, QObject *parent = 0); + ~CdbLocalsModel(); + + // Set a symbolgroup context, thus activating a stack frame. + // Set 0 to clear out. + void setSymbolGroupContext(CdbSymbolGroupContext *sg = 0); + CdbSymbolGroupContext *symbolGroupContext() const { return m_symbolGroupContext; } + + void setUseDumpers(bool d) { m_useDumpers = d; } + +protected: + virtual bool fetchChildren(WatchItem *wd, QString *errorMessage); + virtual bool complete(WatchItem *wd, QString *errorMessage); + +private: + const QSharedPointer<CdbDumperHelper> m_dumperHelper; + CdbSymbolGroupContext *m_symbolGroupContext; + bool m_useDumpers; +}; + +class CdbWatchModel : public AbstractSyncWatchModel { + Q_DISABLE_COPY(CdbWatchModel) + Q_OBJECT +public: + explicit CdbWatchModel(CdbDebugEngine *engine, + const QSharedPointer<CdbDumperHelper> &dh, + WatchHandler *handler, + WatchType type, QObject *parent = 0); + ~CdbWatchModel(); + +public slots: + void addWatcher(const WatchData &d); + void refresh(); + +protected: + virtual bool fetchChildren(WatchItem *wd, QString *errorMessage); + virtual bool complete(WatchItem *wd, QString *errorMessage); + +private: + bool evaluateWatchExpression(WatchData *wd, QString *errorMessage); + + const QSharedPointer<CdbDumperHelper> m_dumperHelper; + CdbDebugEngine *m_engine; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // CDBWATCHMODELS_H diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index 25b7c355f07..c74dcdd01f4 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -44,6 +44,8 @@ HEADERS += \ sourcefileswindow.h \ threadswindow.h \ watchhandler.h \ + asyncwatchmodel.h \ + abstractsyncwatchmodel.h \ watchwindow.h \ SOURCES += \ @@ -71,6 +73,8 @@ SOURCES += \ sourcefileswindow.cpp \ threadswindow.cpp \ watchhandler.cpp \ + asyncwatchmodel.cpp \ + abstractsyncwatchmodel.cpp \ watchwindow.cpp \ FORMS += attachexternaldialog.ui \ diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp index 15c586b5bba..67a9d93efbe 100644 --- a/src/plugins/debugger/debuggermanager.cpp +++ b/src/plugins/debugger/debuggermanager.cpp @@ -187,7 +187,8 @@ static IDebuggerEngine *tcfEngine = 0; DebuggerManager::DebuggerManager() : m_startParameters(new DebuggerStartParameters), - m_inferiorPid(0) + m_inferiorPid(0), + m_watchHandler(new WatchHandler) { init(); } @@ -231,6 +232,8 @@ void DebuggerManager::init() m_sourceFilesWindow = new SourceFilesWindow; m_threadsWindow = new ThreadsWindow; m_localsWindow = new WatchWindow(WatchWindow::LocalsType); + m_watchHandler->init(m_localsWindow); + m_watchersWindow = new WatchWindow(WatchWindow::WatchersType); //m_tooltipWindow = new WatchWindow(WatchWindow::TooltipType); m_statusTimer = new QTimer(this); @@ -321,14 +324,7 @@ void DebuggerManager::init() m_registerHandler = new RegisterHandler; registerView->setModel(m_registerHandler->model()); - // Locals - m_watchHandler = new WatchHandler; - QTreeView *localsView = qobject_cast<QTreeView *>(m_localsWindow); - localsView->setModel(m_watchHandler->model(LocalsWatch)); - - // Watchers - QTreeView *watchersView = qobject_cast<QTreeView *>(m_watchersWindow); - watchersView->setModel(m_watchHandler->model(WatchersWatch)); + // Locals/Watchers connect(m_watchHandler, SIGNAL(sessionValueRequested(QString,QVariant*)), this, SIGNAL(sessionValueRequested(QString,QVariant*))); connect(m_watchHandler, SIGNAL(setSessionValueRequested(QString,QVariant)), @@ -337,12 +333,6 @@ void DebuggerManager::init() this, SLOT(assignValueInDebugger()), Qt::QueuedConnection); // Tooltip - //QTreeView *tooltipView = qobject_cast<QTreeView *>(m_tooltipWindow); - //tooltipView->setModel(m_watchHandler->model(TooltipsWatch)); - - connect(m_watchHandler, SIGNAL(watchDataUpdateNeeded(WatchData)), - this, SLOT(updateWatchData(WatchData))); - m_continueAction = new QAction(this); m_continueAction->setText(tr("Continue")); m_continueAction->setIcon(QIcon(":/debugger/images/debugger_continue_small.png")); @@ -471,12 +461,34 @@ void DebuggerManager::init() localsAndWatchers->setStretchFactor(2, 1); m_watchDock = createDockForWidget(localsAndWatchers); + initializeWatchModels(gdbEngine); + setStatus(DebuggerProcessNotReady); } +void DebuggerManager::initializeWatchModels(IDebuggerEngine *engine) +{ + if (engine == 0) + return; + WatchModel *localsModel = engine->watchModel(LocalsWatch); + m_watchHandler->setModel(LocalsWatch, localsModel); + m_localsWindow->setModel(localsModel); + + WatchModel *watchModel = engine->watchModel(WatchersWatch); + m_watchHandler->setModel(WatchersWatch, watchModel); + m_watchersWindow->setModel(watchModel); + + if (WatchModel *toolTipModel = engine->watchModel(TooltipsWatch)) { + m_watchHandler->setModel(TooltipsWatch, toolTipModel); + } else { + m_watchHandler->setModel(TooltipsWatch, 0); + } +} + QList<Core::IOptionsPage*> DebuggerManager::initializeEngines(unsigned enabledTypeFlags) { QList<Core::IOptionsPage*> rc; + // Initialize watch models so columns show up initially. if (enabledTypeFlags & GdbEngineType) gdbEngine = createGdbEngine(this, &rc); winEngine = createWinEngine(this, (enabledTypeFlags & CdbEngineType), &rc); @@ -487,6 +499,12 @@ QList<Core::IOptionsPage*> DebuggerManager::initializeEngines(unsigned enabledTy m_engine = 0; if (Debugger::Constants::Internal::debug) qDebug() << Q_FUNC_INFO << gdbEngine << winEngine << scriptEngine << rc.size(); + if (gdbEngine) { + initializeWatchModels(gdbEngine); + } else { + if (winEngine) + initializeWatchModels(winEngine); + } return rc; } @@ -802,12 +820,6 @@ void DebuggerManager::setToolTipExpression(const QPoint &mousePos, TextEditor::I m_engine->setToolTipExpression(mousePos, editor, cursorPos); } -void DebuggerManager::updateWatchData(const WatchData &data) -{ - if (m_engine) - m_engine->updateWatchData(data); -} - QVariant DebuggerManager::sessionValue(const QString &name) { // this is answered by the plugin @@ -862,7 +874,7 @@ static IDebuggerEngine *determineDebuggerEngine(const QString &executable, return scriptEngine; } -#ifndef Q_OS_WIN +#ifndef Q_OS_WIN Q_UNUSED(settingsIdHint) if (!gdbEngine) { *errorMessage = msgEngineNotAvailable("Gdb Engine"); @@ -943,6 +955,7 @@ void DebuggerManager::startNewDebugger(DebuggerRunControl *runControl, break; } + if (!m_engine) { debuggingFinished(); // Create Message box with possibility to go to settings @@ -956,6 +969,7 @@ void DebuggerManager::startNewDebugger(DebuggerRunControl *runControl, Core::ICore::instance()->showOptionsDialog(_(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY), settingsIdHint); return; } + initializeWatchModels(m_engine); if (Debugger::Constants::Internal::debug) qDebug() << m_startParameters->executable << m_engine; diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h index c153465c884..3dc8dcb16b9 100644 --- a/src/plugins/debugger/debuggermanager.h +++ b/src/plugins/debugger/debuggermanager.h @@ -46,6 +46,7 @@ class QModelIndex; class QPoint; class QTimer; class QWidget; +class QTreeView; class QDebug; QT_END_NAMESPACE @@ -307,7 +308,6 @@ public slots: void detachDebugger(); void addToWatchWindow(); - void updateWatchData(const WatchData &data); void sessionLoaded(); void sessionUnloaded(); @@ -418,6 +418,7 @@ public: private: void init(); void runTest(const QString &fileName); + void initializeWatchModels(IDebuggerEngine *engine); QDockWidget *createDockForWidget(QWidget *widget); Q_SLOT void createNewDock(QWidget *widget); void updateDockWidget(QDockWidget *dockWidget); @@ -483,13 +484,12 @@ private: QWidget *m_breakWindow; QWidget *m_disassemblerWindow; - QWidget *m_localsWindow; + QTreeView *m_localsWindow; QWidget *m_registerWindow; QWidget *m_modulesWindow; - //QWidget *m_tooltipWindow; QWidget *m_stackWindow; QWidget *m_threadsWindow; - QWidget *m_watchersWindow; + QTreeView *m_watchersWindow; DebuggerOutputWindow *m_outputWindow; int m_status; diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 3b5431012aa..d60877b4dc4 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -46,6 +46,7 @@ #include "registerhandler.h" #include "stackhandler.h" #include "watchhandler.h" +#include "asyncwatchmodel.h" #include "sourcefileswindow.h" #include "debuggerdialogs.h" @@ -152,7 +153,8 @@ GdbEngine::GdbEngine(DebuggerManager *parent) : m_dumperInjectionLoad(false), #endif q(parent), - qq(parent->engineInterface()) + qq(parent->engineInterface()), + m_models(qq->watchHandler()) { m_stubProc.setMode(Core::Utils::ConsoleProcess::Debug); #ifdef Q_OS_UNIX @@ -170,6 +172,8 @@ GdbEngine::~GdbEngine() void GdbEngine::initializeConnections() { + connect(qq->watchHandler(), SIGNAL(watcherInserted(WatchData)), &m_models, SLOT(insertWatcher(WatchData))); + connect(&m_models, SIGNAL(watchDataUpdateNeeded(WatchData)), this, SLOT(updateWatchData(WatchData))); // Gdb Process interaction connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), this, SLOT(gdbProcError(QProcess::ProcessError))); @@ -2696,10 +2700,10 @@ static QString tooltipINameForExpression(const QString &exp) bool GdbEngine::showToolTip() { WatchHandler *handler = qq->watchHandler(); - WatchModel *model = handler->model(TooltipsWatch); + AsyncWatchModel *model = static_cast<AsyncWatchModel *>(handler->model(TooltipsWatch)); QString iname = tooltipINameForExpression(m_toolTipExpression); model->setActiveData(iname); - WatchItem *item = model->findItem(iname, model->dummyRoot()); + WatchItem *item = model->findItemByIName(iname, model->dummyRoot()); if (!item) { hideDebuggerToolTip(); return false; @@ -2788,7 +2792,7 @@ void GdbEngine::setToolTipExpression(const QPoint &mousePos, toolTip.name = exp; toolTip.iname = tooltipINameForExpression(exp); qq->watchHandler()->removeData(toolTip.iname); - qq->watchHandler()->insertData(toolTip); + m_models.insertData(toolTip); } @@ -3146,7 +3150,7 @@ void GdbEngine::rebuildModel() PENDING_DEBUG("REBUILDING MODEL"); emit gdbInputAvailable(LogStatus, _("<Rebuild Watchmodel>")); q->showStatusMessage(tr("Finished retrieving data."), 400); - qq->watchHandler()->endCycle(); + m_models.endCycle(); showToolTip(); } @@ -3536,7 +3540,7 @@ void GdbEngine::updateLocals() PENDING_DEBUG("\nRESET PENDING"); //m_toolTipCache.clear(); m_toolTipExpression.clear(); - qq->watchHandler()->beginCycle(); + m_models.beginCycle(); QString level = QString::number(currentFrame()); // '2' is 'list with type and value' @@ -3590,7 +3594,7 @@ void GdbEngine::handleStackListLocals(const GdbResultRecord &record, const QVari locals += m_currentFunctionArgs; setLocals(locals); - qq->watchHandler()->updateWatchers(); + m_models.updateWatchers(); } void GdbEngine::setLocals(const QList<GdbMi> &locals) @@ -3662,7 +3666,7 @@ void GdbEngine::insertData(const WatchData &data0) qDebug() << "BOGUS VALUE:" << data.toString(); return; } - qq->watchHandler()->insertData(data); + m_models.insertData(data); } void GdbEngine::handleVarListChildrenHelper(const GdbMi &item, diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 41137705879..0ad6dc2e90b 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -34,6 +34,7 @@ #include "gdbmi.h" #include "outputcollector.h" #include "watchutils.h" +#include "asyncwatchmodel.h" #include <consoleprocess.h> @@ -122,6 +123,8 @@ private: void loadAllSymbols(); virtual QList<Symbol> moduleSymbols(const QString &moduleName); + WatchModel *watchModel(int type) const { return m_models.model(type); } + Q_SLOT void setDebugDebuggingHelpers(const QVariant &on); Q_SLOT void setUseDebuggingHelpers(const QVariant &on); @@ -197,6 +200,7 @@ private slots: void stubStarted(); void stubError(const QString &msg); void uploadProcError(QProcess::ProcessError error); + void updateWatchData(const WatchData &data); private: int terminationIndex(const QByteArray &buffer, int &length); @@ -324,7 +328,6 @@ private: // FIXME: BaseClass. called to improve situation for a watch item void updateSubItem(const WatchData &data); - void updateWatchData(const WatchData &data); void rebuildModel(); void insertData(const WatchData &data); @@ -384,6 +387,7 @@ private: DebuggerManager * const q; IDebuggerManagerAccessForEngines * const qq; + AsyncWatchModelMixin m_models; // make sure to re-initialize new members in initializeVariables(); }; diff --git a/src/plugins/debugger/idebuggerengine.h b/src/plugins/debugger/idebuggerengine.h index dbbdb143143..f99e26bd869 100644 --- a/src/plugins/debugger/idebuggerengine.h +++ b/src/plugins/debugger/idebuggerengine.h @@ -46,6 +46,8 @@ class ITextEditor; namespace Debugger { namespace Internal { +class WatchModel; + class Symbol; class WatchData; struct DebuggerStartParameters; @@ -60,14 +62,13 @@ public: virtual bool startDebugger(const QSharedPointer<DebuggerStartParameters> &startParameters) = 0; virtual void exitDebugger() = 0; virtual void detachDebugger() {} - virtual void updateWatchData(const WatchData &data) = 0; virtual void stepExec() = 0; virtual void stepOutExec() = 0; virtual void nextExec() = 0; virtual void stepIExec() = 0; virtual void nextIExec() = 0; - + virtual void continueInferior() = 0; virtual void interruptInferior() = 0; @@ -95,6 +96,8 @@ public: virtual void reloadFullStack() = 0; virtual void watchPoint(const QPoint &) {} + + virtual WatchModel *watchModel(int type) const = 0; }; } // namespace Internal diff --git a/src/plugins/debugger/script/scriptengine.cpp b/src/plugins/debugger/script/scriptengine.cpp index 28d20a24cc2..0096e3a414d 100644 --- a/src/plugins/debugger/script/scriptengine.cpp +++ b/src/plugins/debugger/script/scriptengine.cpp @@ -185,10 +185,13 @@ void ScriptAgent::scriptUnload(qint64 scriptId) // /////////////////////////////////////////////////////////////////////// -ScriptEngine::ScriptEngine(DebuggerManager *parent) +ScriptEngine::ScriptEngine(DebuggerManager *parent) : + q(parent), + qq(parent->engineInterface()), + m_models(qq->watchHandler()) { - q = parent; - qq = parent->engineInterface(); + connect(qq->watchHandler(), SIGNAL(watcherInserted(WatchData)), &m_models, SLOT(insertWatcher(WatchData))); + connect(&m_models, SIGNAL(watchDataUpdateNeeded(WatchData)), this, SLOT(updateWatchData(WatchData))); m_scriptEngine = new QScriptEngine(this); m_scriptAgent = new ScriptAgent(this, m_scriptEngine); m_scriptEngine->setAgent(m_scriptAgent); @@ -572,7 +575,7 @@ void ScriptEngine::maybeBreakNow(bool byFunction) void ScriptEngine::updateLocals() { QScriptContext *context = m_scriptEngine->currentContext(); - qq->watchHandler()->beginCycle(); + m_models.beginCycle(); //SDEBUG("UPDATE LOCALS"); // @@ -604,7 +607,7 @@ void ScriptEngine::updateLocals() data.iname = "local"; data.name = "local"; data.scriptValue = context->activationObject(); - qq->watchHandler()->insertData(data); + m_models.insertData(data); // FIXME: Use an extra thread. This here is evil m_stopped = true; @@ -679,7 +682,7 @@ void ScriptEngine::updateSubItem(const WatchData &data0) data.setType("<unknown>"); data.setValue("<unknown>"); } - qq->watchHandler()->insertData(data); + m_models.insertData(data); return; } @@ -697,13 +700,13 @@ void ScriptEngine::updateSubItem(const WatchData &data0) data1.setChildrenNeeded(); else data1.setChildrenUnneeded(); - qq->watchHandler()->insertData(data1); + m_models.insertData(data1); ++numChild; } //SDEBUG(" ... CHILDREN: " << numChild); data.setHasChildren(numChild > 0); data.setChildrenUnneeded(); - qq->watchHandler()->insertData(data); + m_models.insertData(data); return; } @@ -716,7 +719,7 @@ void ScriptEngine::updateSubItem(const WatchData &data0) } data.setHasChildren(numChild > 0); //SDEBUG(" ... CHILDCOUNT: " << numChild); - qq->watchHandler()->insertData(data); + m_models.insertData(data); return; } diff --git a/src/plugins/debugger/script/scriptengine.h b/src/plugins/debugger/script/scriptengine.h index ae4a434efc0..bd49d0e1ef1 100644 --- a/src/plugins/debugger/script/scriptengine.h +++ b/src/plugins/debugger/script/scriptengine.h @@ -49,6 +49,7 @@ class QScriptValue; QT_END_NAMESPACE #include "idebuggerengine.h" +#include "asyncwatchmodel.h" namespace Debugger { namespace Internal { @@ -107,14 +108,20 @@ private: bool supportsThreads() const { return true; } void maybeBreakNow(bool byFunction); - void updateWatchData(const WatchData &data); + void updateLocals(); void updateSubItem(const WatchData &data); + WatchModel *watchModel(int type) const { return m_models.model(type); } + +private slots: + void updateWatchData(const WatchData &data); + private: friend class ScriptAgent; DebuggerManager *q; IDebuggerManagerAccessForEngines *qq; + AsyncWatchModelMixin m_models; QScriptEngine *m_scriptEngine; QString m_scriptContents; diff --git a/src/plugins/debugger/tcf/tcfengine.cpp b/src/plugins/debugger/tcf/tcfengine.cpp index b699f40954a..756f0d80289 100644 --- a/src/plugins/debugger/tcf/tcfengine.cpp +++ b/src/plugins/debugger/tcf/tcfengine.cpp @@ -112,11 +112,13 @@ QString TcfEngine::TcfCommand::toString() const // /////////////////////////////////////////////////////////////////////// -TcfEngine::TcfEngine(DebuggerManager *parent) +TcfEngine::TcfEngine(DebuggerManager *parent) : + q(parent), + qq(parent->engineInterface()), + m_models(qq->watchHandler()) { - q = parent; - qq = parent->engineInterface(); - + connect(qq->watchHandler(), SIGNAL(watcherInserted(WatchData)), &m_models, SLOT(insertWatcher(WatchData))); + connect(&m_models, SIGNAL(watchDataUpdateNeeded(WatchData)), this, SLOT(updateWatchData(WatchData))); m_congestion = 0; m_inAir = 0; diff --git a/src/plugins/debugger/tcf/tcfengine.h b/src/plugins/debugger/tcf/tcfengine.h index ff5844c436b..e09097df959 100644 --- a/src/plugins/debugger/tcf/tcfengine.h +++ b/src/plugins/debugger/tcf/tcfengine.h @@ -30,6 +30,8 @@ #ifndef DEBUGGER_TCFENGINE_H #define DEBUGGER_TCFENGINE_H +#include "asyncwatchmodel.h" + #include <QtCore/QByteArray> #include <QtCore/QHash> #include <QtCore/QMap> @@ -112,10 +114,11 @@ private: bool supportsThreads() const { return true; } void maybeBreakNow(bool byFunction); - void updateWatchData(const WatchData &data); void updateLocals(); void updateSubItem(const WatchData &data); + WatchModel *watchModel(int type) const { return m_models.model(type); } + Q_SLOT void socketConnected(); Q_SLOT void socketDisconnected(); Q_SLOT void socketError(QAbstractSocket::SocketError); @@ -128,6 +131,7 @@ private: private: Q_SLOT void startDebugging(); + Q_SLOT void updateWatchData(const WatchData &data); typedef void (TcfEngine::*TcfCommandCallback) (const JsonValue &record, const QVariant &cookie); @@ -153,7 +157,7 @@ private: QHash<int, TcfCommand> m_cookieForToken; QQueue<TcfCommand> m_sendQueue; - + // timer based congestion control. does not seem to work well. void enqueueCommand(const TcfCommand &command); Q_SLOT void handleSendTimer(); @@ -166,6 +170,7 @@ private: DebuggerManager *q; IDebuggerManagerAccessForEngines *qq; + AsyncWatchModelMixin m_models; QTcpSocket *m_socket; QByteArray m_inbuffer; QList<QByteArray> m_services; diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index bad769c7dbb..c33f8bcc486 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -48,6 +48,7 @@ #include <QtGui/QLabel> #include <QtGui/QToolTip> #include <QtGui/QTextEdit> +#include <QtGui/QTreeView> #include <ctype.h> @@ -67,40 +68,13 @@ namespace Debugger { namespace Internal { -static const QString strNotInScope = - QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>"); - static int watcherCounter = 0; -static int generationCounter = 0; - -//////////////////////////////////////////////////////////////////// -// -// WatchItem -// -//////////////////////////////////////////////////////////////////// - -class WatchItem : public WatchData -{ -public: - WatchItem() { parent = 0; fetchTriggered = false; } - - WatchItem(const WatchData &data) : WatchData(data) - { parent = 0; fetchTriggered = false; } - - void setData(const WatchData &data) - { static_cast<WatchData &>(*this) = data; } - - WatchItem *parent; - bool fetchTriggered; // children fetch has been triggered - QList<WatchItem *> children; // fetched children -}; - //////////////////////////////////////////////////////////////////// // // WatchData // //////////////////////////////////////////////////////////////////// - + WatchData::WatchData() : hasChildren(false), generation(-1), @@ -148,7 +122,7 @@ void WatchData::setValue(const QString &value0) // column. No need to duplicate it here. if (value.startsWith("(" + type + ") 0x")) value = value.section(" ", -1, -1); - + setValueUnneeded(); } @@ -272,109 +246,125 @@ QString WatchData::toToolTip() const return res; } -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// // -// WatchModel +// WatchItem // -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// -WatchModel::WatchModel(WatchHandler *handler, WatchType type) - : QAbstractItemModel(handler), m_handler(handler), m_type(type) +WatchItem::WatchItem() : + parent(0), + fetchTriggered(false) { - m_root = new WatchItem; - m_root->hasChildren = 1; - m_root->state = 0; - m_root->name = WatchHandler::tr("Root"); +} - WatchItem *item = new WatchItem; +WatchItem::WatchItem(const WatchData &data) : + WatchData(data), + parent(0), + fetchTriggered(false) +{ +} - switch (m_type) { - case LocalsWatch: - item->iname = QLatin1String("local"); - item->name = WatchHandler::tr("Locals"); - break; - case WatchersWatch: - item->iname = QLatin1String("watch"); - item->name = WatchHandler::tr("Watchers"); - break; - case TooltipsWatch: - item->iname = QLatin1String("tooltip"); - item->name = WatchHandler::tr("Tooltip"); - break; - } - item->hasChildren = true; - item->state = 0; - item->parent = m_root; - item->fetchTriggered = true; +void WatchItem::setData(const WatchData &data) +{ + static_cast<WatchData &>(*this) = data; +} - m_root->children.append(item); +WatchItem::~WatchItem() +{ + qDeleteAll(children); } -WatchItem *WatchModel::dummyRoot() const +void WatchItem::removeChildren() { - QTC_ASSERT(!m_root->children.isEmpty(), return 0); - return m_root->children.front(); + if (!children.empty()) { + qDeleteAll(children); + children.clear(); + } } -void WatchModel::reinitialize() +void WatchItem::addChild(WatchItem *child) { - WatchItem *item = dummyRoot(); - QTC_ASSERT(item, return); - QModelIndex index = watchIndex(item); - int n = item->children.size(); - if (n == 0) - return; - //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << item->iname); - beginRemoveRows(index, 0, n - 1); - qDeleteAll(item->children); - item->children.clear(); - endRemoveRows(); + children.push_back(child); + child->parent = this; } -void WatchModel::removeOutdated() +/////////////////////////////////////////////////////////////////////// +// +// WatchPredicate +// +/////////////////////////////////////////////////////////////////////// + +WatchPredicate::WatchPredicate(Mode mode, const QString &pattern) : + m_pattern(pattern), + m_mode(mode) { - WatchItem *item = dummyRoot(); - QTC_ASSERT(item, return); - foreach (WatchItem *child, item->children) - removeOutdatedHelper(child); -#if DEBUG_MODEL -#if USE_MODEL_TEST - //(void) new ModelTest(this, this); -#endif -#endif } -void WatchModel::removeOutdatedHelper(WatchItem *item) +bool WatchPredicate::operator()(const WatchData &w) const { - if (item->generation < generationCounter) - removeItem(item); - else { - foreach (WatchItem *child, item->children) - removeOutdatedHelper(child); - item->fetchTriggered = false; + switch (m_mode) { + case INameMatch: + return m_pattern == w.iname; + case ExpressionMatch: + return m_pattern == w.exp; } + return false; } -void WatchModel::removeItem(WatchItem *item) +/////////////////////////////////////////////////////////////////////// +// +// WatchModel +// +/////////////////////////////////////////////////////////////////////// + +WatchModel::WatchModel(WatchHandler *handler, WatchType type, QObject *parent) : + QAbstractItemModel(parent), + m_root(new WatchItem), + m_handler(handler), + m_type(type) { - WatchItem *parent = item->parent; - QModelIndex index = watchIndex(parent); - int n = parent->children.indexOf(item); - //MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n); - beginRemoveRows(index, n, n); - parent->children.removeAt(n); - endRemoveRows(); + WatchItem *dummyRoot = new WatchItem; + + switch (type) { + case LocalsWatch: + dummyRoot->iname = QLatin1String("local"); + dummyRoot->name = WatchHandler::tr("Locals"); + break; + case WatchersWatch: + dummyRoot->iname = QLatin1String("watch"); + dummyRoot->name = WatchHandler::tr("Watchers"); + break; + case TooltipsWatch: + dummyRoot->iname = QLatin1String("tooltip"); + dummyRoot->name = WatchHandler::tr("Tooltip"); + break; + case WatchModelCount: + break; + } + dummyRoot->hasChildren = true; + dummyRoot->state = 0; + + m_root->addChild(dummyRoot); + + m_root->hasChildren = 1; + m_root->state = 0; + m_root->name = WatchHandler::tr("Root"); } -static QString parentName(const QString &iname) +WatchModel::~WatchModel() { - int pos = iname.lastIndexOf(QLatin1Char('.')); + delete m_root; +} + +QString WatchModel::parentName(const QString &iname) +{ + const int pos = iname.lastIndexOf(QLatin1Char('.')); if (pos == -1) return QString(); return iname.left(pos); } - static QString chopConst(QString type) { while (1) { @@ -419,7 +409,7 @@ QString niceType(const QString typeIn) for (int i = 0; i < 10; ++i) { int start = type.indexOf("std::allocator<"); if (start == -1) - break; + break; // search for matching '>' int pos; int level = 0; @@ -527,51 +517,10 @@ static QString formattedValue(const WatchData &data, return data.value; } -bool WatchModel::canFetchMore(const QModelIndex &index) const -{ - return index.isValid() && !watchItem(index)->fetchTriggered; -} - -void WatchModel::fetchMore(const QModelIndex &index) -{ - QTC_ASSERT(index.isValid(), return); - QTC_ASSERT(!watchItem(index)->fetchTriggered, return); - if (WatchItem *item = watchItem(index)) { - item->fetchTriggered = true; - WatchData data = *item; - data.setChildrenNeeded(); - emit m_handler->watchDataUpdateNeeded(data); - } -} - -QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const -{ - if (!hasIndex(row, column, parent)) - return QModelIndex(); - - const WatchItem *item = watchItem(parent); - QTC_ASSERT(item, return QModelIndex()); - return createIndex(row, column, (void*)(item->children.at(row))); -} - -QModelIndex WatchModel::parent(const QModelIndex &idx) const +int WatchModel::columnCount(const QModelIndex &idx) const { - if (!idx.isValid()) - return QModelIndex(); - - const WatchItem *item = watchItem(idx); - if (!item->parent || item->parent == m_root) - return QModelIndex(); - - const WatchItem *grandparent = item->parent->parent; - if (!grandparent) - return QModelIndex(); - - for (int i = 0; i < grandparent->children.size(); ++i) - if (grandparent->children.at(i) == item->parent) - return createIndex(i, 0, (void*) item->parent); - - return QModelIndex(); + Q_UNUSED(idx); + return 3; } int WatchModel::rowCount(const QModelIndex &idx) const @@ -583,45 +532,18 @@ int WatchModel::rowCount(const QModelIndex &idx) const return watchItem(idx)->children.size(); } -int WatchModel::columnCount(const QModelIndex &idx) const -{ - Q_UNUSED(idx); - return 3; -} - -bool WatchModel::hasChildren(const QModelIndex &parent) const -{ - WatchItem *item = watchItem(parent); - return !item || item->hasChildren; -} - -WatchItem *WatchModel::watchItem(const QModelIndex &idx) const +bool WatchModel::hasChildren(const QModelIndex &idx) const { - return idx.isValid() - ? static_cast<WatchItem*>(idx.internalPointer()) : m_root; -} - -QModelIndex WatchModel::watchIndex(const WatchItem *item) const -{ - return watchIndexHelper(item, m_root, QModelIndex()); -} - -QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle, - const WatchItem *parentItem, const QModelIndex &parentIndex) const -{ - if (needle == parentItem) - return parentIndex; - for (int i = parentItem->children.size(); --i >= 0; ) { - const WatchItem *childItem = parentItem->children.at(i); - QModelIndex childIndex = index(i, 0, parentIndex); - QModelIndex idx = watchIndexHelper(needle, childItem, childIndex); - if (idx.isValid()) - return idx; + if (const WatchItem *item = watchItem(idx)) { + if (!item->children.empty()) + return true; + QTC_ASSERT(item->isHasChildrenKnown(), return false); + return item->hasChildren; } - return QModelIndex(); + return false; } -void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex) +void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex) { QModelIndex idx1 = index(0, column, parentIndex); QModelIndex idx2 = index(rowCount(parentIndex) - 1, column, parentIndex); @@ -635,11 +557,16 @@ void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex) QVariant WatchModel::data(const QModelIndex &idx, int role) const { - const WatchItem &data = *watchItem(idx); + if (const WatchData *wdata = watchItem(idx)) + return data(*wdata, idx.column(), role); + return QVariant(); +} +QVariant WatchModel::data(const WatchData &data, int column, int role) const +{ switch (role) { case Qt::DisplayRole: { - switch (idx.column()) { + switch (column) { case 0: return data.name; case 1: return formattedValue(data, m_handler->m_individualFormats[data.iname], @@ -657,7 +584,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const static const QVariant red(QColor(200, 0, 0)); static const QVariant black(QColor(0, 0, 0)); static const QVariant gray(QColor(140, 140, 140)); - switch (idx.column()) { + switch (column) { case 0: return black; case 1: return data.valuedisabled ? gray : data.changed ? red : black; case 2: return black; @@ -678,7 +605,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const case ActiveDataRole: qDebug() << "ASK FOR" << data.iname; return true; - + case TypeFormatListRole: if (isIntType(data.type)) return QStringList() << tr("decimal") << tr("hexadecimal") @@ -696,14 +623,14 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const } default: - break; + break; } return QVariant(); } bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role) { - WatchItem &data = *watchItem(index); + WatchData &data = *watchItem(index); if (role == ExpandedRole) { if (value.toBool()) m_handler->m_expandedINames.insert(data.iname); @@ -760,75 +687,162 @@ QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int ro case 2: return QString(tr("Type") + QLatin1String(" ")); } } - return QVariant(); + return QVariant(); +} + +void WatchModel::setValueByIName(const QString &iname, const QString &value) +{ + if (WatchItem *wd = findItemByIName(iname, dummyRoot())) { + if (wd->value != value) { + wd->setValue(value); + const QModelIndex index = watchIndex(wd); + emit dataChanged(index.sibling(0, 1), index.sibling(0, 2)); + } + } } -static bool iNameSorter(const WatchItem *item1, const WatchItem *item2) +void WatchModel::setValueByExpression(const QString &exp, const QString &value) { - QString name1 = item1->iname.section('.', -1); - QString name2 = item2->iname.section('.', -1); - if (!name1.isEmpty() && !name2.isEmpty()) { - if (name1.at(0).isDigit() && name2.at(0).isDigit()) - return name1.toInt() < name2.toInt(); + if (WatchItem *wd = findItemByExpression(exp, dummyRoot())) { + if (wd->value != value) { + wd->setValue(value); + const QModelIndex index = watchIndex(wd); + emit dataChanged(index.sibling(0, 1), index.sibling(0, 2)); + } } - return name1 < name2; } -static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item) +WatchItem *WatchModel::root() const { - QList<WatchItem *>::const_iterator it = - qLowerBound(list.begin(), list.end(), item, iNameSorter); - return it - list.begin(); + return m_root; } -void WatchModel::insertData(const WatchData &data) +WatchItem *WatchModel::dummyRoot() const +{ + if (!m_root->children.isEmpty()) + return m_root->children.front(); + return 0; +} + +void WatchModel::reinitialize() { - // qDebug() << "WMI:" << data.toString(); - QTC_ASSERT(!data.iname.isEmpty(), return); - WatchItem *parent = findItem(parentName(data.iname), m_root); - if (!parent) { - WatchData parent; - parent.iname = parentName(data.iname); - insertData(parent); - //MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname); + WatchItem *item = dummyRoot(); + QTC_ASSERT(item, return); + const QModelIndex index = watchIndex(item); + const int n = item->children.size(); + if (n == 0) return; + //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << item->iname); + beginRemoveRows(index, 0, n - 1); + item->removeChildren(); + endRemoveRows(); +} + +void WatchModel::removeItem(WatchItem *itemIn) +{ + WatchItem *item = static_cast<WatchItem *>(itemIn); + WatchItem *parent = item->parent; + const QModelIndex index = watchIndex(parent); + const int n = parent->children.indexOf(item); + //MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n); + beginRemoveRows(index, n, n); + parent->children.removeAt(n); + endRemoveRows(); +} + +QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + const WatchItem *item = watchItem(parent); + QTC_ASSERT(item, return QModelIndex()); + return createIndex(row, column, (void*)(item->children.at(row))); +} + +QModelIndex WatchModel::parent(const QModelIndex &idx) const +{ + if (!idx.isValid()) + return QModelIndex(); + + const WatchItem *item = static_cast<WatchItem *>(watchItem(idx)); + if (!item->parent || item->parent == m_root) + return QModelIndex(); + + const WatchItem *grandparent = item->parent->parent; + if (!grandparent) + return QModelIndex(); + + for (int i = 0; i < grandparent->children.size(); ++i) + if (grandparent->children.at(i) == item->parent) + return createIndex(i, 0, (void*) item->parent); + + return QModelIndex(); +} + +WatchItem *WatchModel::watchItem(const QModelIndex &idx) const + +{ + return idx.isValid() + ? static_cast<WatchItem*>(idx.internalPointer()) : m_root; +} + +QModelIndex WatchModel::watchIndex(const WatchItem *item) const +{ + return watchIndexHelper(item, m_root, QModelIndex()); +} + +QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle, + const WatchItem *parentItem, const QModelIndex &parentIndex) const +{ + if (needle == parentItem) + return parentIndex; + for (int i = parentItem->children.size(); --i >= 0; ) { + const WatchItem *childItem = parentItem->children.at(i); + QModelIndex childIndex = index(i, 0, parentIndex); + QModelIndex idx = watchIndexHelper(needle, childItem, childIndex); + if (idx.isValid()) + return idx; } - QModelIndex index = watchIndex(parent); - if (WatchItem *oldItem = findItem(data.iname, parent)) { - // overwrite old entry - //MODEL_DEBUG("OVERWRITE : " << data.iname << data.value); - bool changed = !data.value.isEmpty() - && data.value != oldItem->value - && data.value != strNotInScope; - oldItem->setData(data); - oldItem->changed = changed; - oldItem->generation = generationCounter; - QModelIndex idx = watchIndex(oldItem); - emit dataChanged(idx, idx.sibling(idx.row(), 2)); - } else { - // add new entry - //MODEL_DEBUG("INSERT : " << data.iname << data.value); - WatchItem *item = new WatchItem(data); - item->parent = parent; - item->generation = generationCounter; - item->changed = true; - int n = findInsertPosition(parent->children, item); - beginInsertRows(index, n, n); - parent->children.insert(n, item); - endInsertRows(); - } + return QModelIndex(); } -WatchItem *WatchModel::findItem(const QString &iname, WatchItem *root) const +static WatchItem *findRecursion(WatchItem *root, const WatchPredicate &p) { - if (root->iname == iname) + if (p(*root)) return root; for (int i = root->children.size(); --i >= 0; ) - if (WatchItem *item = findItem(iname, root->children.at(i))) + if (WatchItem *item = findRecursion(root->children.at(i), p)) return item; return 0; } +WatchItem *WatchModel::findItemByExpression(const QString &exp, WatchItem *root) const +{ + return findRecursion(root, WatchPredicate(WatchPredicate::ExpressionMatch, exp)); +} + +WatchItem *WatchModel::findItemByIName(const QString &iname, WatchItem *root) const +{ + return findRecursion(root, WatchPredicate(WatchPredicate::INameMatch, iname)); +} + +static void debugItemRecursion(QDebug d, WatchItem *item, QString indent = QString()) +{ + qDebug() << (indent + item->toString()); + if (!item->children.isEmpty()) { + indent += QLatin1String(" "); + foreach(WatchItem *c, item->children) + debugItemRecursion(d, c, indent); + } +} + +QDebug operator<<(QDebug d, const WatchModel &wm) +{ + if (WatchItem *root = wm.dummyRoot()) + debugItemRecursion(d.nospace(), root); + return d; +} /////////////////////////////////////////////////////////////////////// // @@ -836,34 +850,25 @@ WatchItem *WatchModel::findItem(const QString &iname, WatchItem *root) const // /////////////////////////////////////////////////////////////////////// -WatchHandler::WatchHandler() +WatchHandler::WatchHandler() : + m_expandPointers(true), + m_inChange(false) { - m_expandPointers = true; - m_inChange = false; - - m_locals = new WatchModel(this, LocalsWatch); - m_watchers = new WatchModel(this, WatchersWatch); - m_tooltips = new WatchModel(this, TooltipsWatch); - connect(theDebuggerAction(WatchExpression), SIGNAL(triggered()), this, SLOT(watchExpression())); connect(theDebuggerAction(RemoveWatchExpression), SIGNAL(triggered()), this, SLOT(removeWatchExpression())); -} - -void WatchHandler::endCycle() -{ - m_locals->removeOutdated(); - m_watchers->removeOutdated(); - m_tooltips->removeOutdated(); + qFill(m_models, m_models + WatchModelCount, static_cast<WatchModel*>(0)); } void WatchHandler::cleanup() { m_expandedINames.clear(); m_displayedINames.clear(); - m_locals->reinitialize(); - m_tooltips->reinitialize(); + if (m_models[LocalsWatch]) + m_models[LocalsWatch]->reinitialize(); + if (m_models[TooltipsWatch]) + m_models[TooltipsWatch]->reinitialize(); #if 0 for (EditWindows::ConstIterator it = m_editWindows.begin(); it != m_editWindows.end(); ++it) { @@ -874,26 +879,12 @@ void WatchHandler::cleanup() #endif } -void WatchHandler::insertData(const WatchData &data) -{ - MODEL_DEBUG("INSERTDATA: " << data.toString()); - QTC_ASSERT(data.isValid(), return); - if (data.isSomethingNeeded()) { - emit watchDataUpdateNeeded(data); - } else { - WatchModel *model = modelForIName(data.iname); - QTC_ASSERT(model, return); - model->insertData(data); - } -} - void WatchHandler::removeData(const QString &iname) { WatchModel *model = modelForIName(iname); if (!model) return; - WatchItem *item = model->findItem(iname, model->m_root); - if (item) + if (WatchItem *item = model->findItemByIName(iname, model->root())) model->removeItem(item); } @@ -918,7 +909,7 @@ void WatchHandler::watchExpression(const QString &exp) if (exp.isEmpty() || exp == watcherEditPlaceHolder()) data.setAllUnneeded(); data.iname = watcherName(exp); - insertData(data); + emit watcherInserted(data); saveWatchers(); //emit watchModelUpdateRequested(); } @@ -1008,33 +999,11 @@ void WatchHandler::removeWatchExpression(const QString &exp) { MODEL_DEBUG("REMOVE WATCH: " << exp); m_watcherNames.remove(exp); - foreach (WatchItem *item, m_watchers->dummyRoot()->children) { - if (item->exp == exp) { - m_watchers->removeItem(item); + if (WatchModel *model = m_models[WatchersWatch]) + if (WatchItem *item = model->findItemByExpression(exp, model->root())) { + model->removeItem(item); saveWatchers(); - break; } - } -} - -void WatchHandler::beginCycle() -{ - ++generationCounter; - //m_locals->beginCycle(); -} - -void WatchHandler::updateWatchers() -{ - //qDebug() << "UPDATE WATCHERS"; - // copy over all watchers and mark all watchers as incomplete - foreach (const QString &exp, m_watcherNames.keys()) { - WatchData data; - data.iname = watcherName(exp); - data.setAllNeeded(); - data.name = exp; - data.exp = exp; - insertData(data); - } } void WatchHandler::loadWatchers() @@ -1076,6 +1045,11 @@ void WatchHandler::loadTypeFormats() } } +QStringList WatchHandler::watcherExpressions() const +{ + return m_watcherNames.keys(); +} + void WatchHandler::saveTypeFormats() { QMap<QString, QVariant> typeFormats; @@ -1099,44 +1073,61 @@ void WatchHandler::loadSessionData() { loadWatchers(); loadTypeFormats(); + initWatchModel(); +} + +void WatchHandler::initWatchModel() +{ foreach (const QString &exp, m_watcherNames.keys()) { WatchData data; data.iname = watcherName(exp); data.setAllUnneeded(); data.name = exp; data.exp = exp; - insertData(data); + emit watcherInserted(data); } } WatchModel *WatchHandler::model(WatchType type) const { - switch (type) { - case LocalsWatch: return m_locals; - case WatchersWatch: return m_watchers; - case TooltipsWatch: return m_tooltips; - } - QTC_ASSERT(false, /**/); - return 0; + return m_models[type]; } - -WatchModel *WatchHandler::modelForIName(const QString &iname) const + +void WatchHandler::setModel(WatchType type, WatchModel *model) +{ + if (m_models[type] == model) + return; + if (type == WatchersWatch && m_models[type]) + saveWatchers(); + m_models[type] = model; + if (type == WatchersWatch) + initWatchModel(); +} + +WatchType WatchHandler::watchTypeOfIName(const QString &iname) { if (iname.startsWith(QLatin1String("local."))) - return m_locals; + return LocalsWatch; if (iname.startsWith(QLatin1String("watch."))) - return m_watchers; + return WatchersWatch; if (iname.startsWith(QLatin1String("tooltip"))) - return m_tooltips; - QTC_ASSERT(false, /**/); - return 0; + return TooltipsWatch; + return WatchModelCount; +} + +WatchModel *WatchHandler::modelForIName(const QString &iname) const +{ + const WatchType t = watchTypeOfIName(iname); + if (t == WatchModelCount) + return 0; + return m_models[t]; } WatchData *WatchHandler::findItem(const QString &iname) const { const WatchModel *model = modelForIName(iname); QTC_ASSERT(model, return 0); - return model->findItem(iname, model->m_root); + return model->findItemByIName(iname, model->root()); } QString WatchHandler::watcherEditPlaceHolder() @@ -1149,9 +1140,55 @@ void WatchHandler::setFormat(const QString &type, int format) { m_typeFormats[type] = format; saveTypeFormats(); - m_locals->emitDataChanged(1); - m_watchers->emitDataChanged(1); - m_tooltips->emitDataChanged(1); + for (int m = 0; m < WatchModelCount; m++) + m_models[m]->emitDataChanged(1); +} + +void WatchHandler::init(QTreeView *localsView) +{ + m_localsView = localsView; +} + +void WatchHandler::saveLocalsViewState(int frame) +{ + WatchModel *model = m_models[LocalsWatch]; + if (!model || !m_localsView) + return; + LocalsViewStateMap::iterator it = m_localsViewState.find(frame); + if (it == m_localsViewState.end()) + it = m_localsViewState.insert(frame, LocalsViewState()); + + const QModelIndex topIndex = m_localsView->indexAt(QPoint(0, 0)); + it.value().firstVisibleRow = topIndex.isValid() ? topIndex.row() : 0; + it.value().expandedINames = m_expandedINames; +} + +void WatchHandler::restoreLocalsViewState(int frame) +{ + WatchModel *model = m_models[LocalsWatch]; + if (!model || !m_localsView) + return; + int firstVisibleRow = 0; + const LocalsViewStateMap::const_iterator it = m_localsViewState.constFind(frame); + if (it != m_localsViewState.constEnd()) { + firstVisibleRow = it.value().firstVisibleRow; + m_expandedINames = it.value().expandedINames; + } + // Loop over and expand valid inames, purge out invalid. + WatchItem *root = model->root(); + for (QSet<QString>::iterator it = m_expandedINames.begin(); it != m_expandedINames.end(); ) { + if (const WatchItem *wd = model->findItemByIName(*it, root)) { + m_localsView->expand(model->watchIndex(wd)); + ++it; + } else { + it = m_expandedINames.erase(it); + } + } + if (firstVisibleRow) { + const QModelIndex index = model->index(0, 0).child(firstVisibleRow, 0); + if (index.isValid()) + m_localsView->scrollTo(index, QAbstractItemView::PositionAtTop); + } } } // namespace Internal diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h index 86e7dff3b07..4b472fab3f4 100644 --- a/src/plugins/debugger/watchhandler.h +++ b/src/plugins/debugger/watchhandler.h @@ -32,19 +32,21 @@ #include <QtCore/QPointer> #include <QtCore/QObject> +#include <QtCore/QStringList> #include <QtCore/QHash> #include <QtCore/QSet> -#include <QtGui/QStandardItem> -#include <QtGui/QStandardItemModel> -#include <QtGui/QTreeView> +#include <QtCore/QAbstractItemModel> #include <QtScript/QScriptValue> +QT_BEGIN_NAMESPACE +class QTreeView; +QT_END_NAMESPACE + namespace Debugger { namespace Internal { -class WatchItem; class WatchHandler; -enum WatchType { LocalsWatch, WatchersWatch, TooltipsWatch }; +enum WatchType { LocalsWatch, WatchersWatch, TooltipsWatch, WatchModelCount }; class WatchData { @@ -160,51 +162,95 @@ enum DumpableFormat PlainFomat, }; +class WatchHandler; + +// Item used by the model. +class WatchItem : public WatchData +{ + Q_DISABLE_COPY(WatchItem) +public: + WatchItem(); + explicit WatchItem(const WatchData &data); + + ~WatchItem(); + + void setData(const WatchData &data); + void addChild(WatchItem *child); + void removeChildren(); + + WatchItem *parent; + bool fetchTriggered; + QList<WatchItem*> children; +}; + +/* WatchModel: To be used by synchonous debuggers. + * Implements all of WatchModel and provides new virtuals for + * the debugger to complete items. */ + class WatchModel : public QAbstractItemModel { Q_OBJECT +public: + explicit WatchModel(WatchHandler *handler, WatchType type, QObject *parent = 0); + virtual ~WatchModel(); -private: - explicit WatchModel(WatchHandler *handler, WatchType type); - - QVariant data(const QModelIndex &index, int role) const; - bool setData(const QModelIndex &index, const QVariant &value, int role); - QModelIndex index(int, int, const QModelIndex &idx) const; - QModelIndex parent(const QModelIndex &idx) const; - int rowCount(const QModelIndex &idx) const; - int columnCount(const QModelIndex &idx) const; - bool hasChildren(const QModelIndex &idx) const; - Qt::ItemFlags flags(const QModelIndex &idx) const; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - bool canFetchMore(const QModelIndex &parent) const; - void fetchMore(const QModelIndex &parent); - - friend class WatchHandler; - friend class GdbEngine; + virtual QModelIndex index(int, int, const QModelIndex &idx = QModelIndex()) const; + + virtual int columnCount(const QModelIndex &idx= QModelIndex()) const; + virtual int rowCount(const QModelIndex &idx) const; + virtual bool hasChildren(const QModelIndex &idx) const; + virtual QVariant data(const QModelIndex &index, int role) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role); + + virtual Qt::ItemFlags flags(const QModelIndex &idx) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + virtual QModelIndex parent(const QModelIndex &idx) const; WatchItem *watchItem(const QModelIndex &) const; QModelIndex watchIndex(const WatchItem *needle) const; QModelIndex watchIndexHelper(const WatchItem *needle, const WatchItem *parentItem, const QModelIndex &parentIndex) const; - void insertData(const WatchData &data); - WatchItem *findItem(const QString &iname, WatchItem *root) const; + WatchItem *findItemByIName(const QString &iname, WatchItem *root) const; + WatchItem *findItemByExpression(const QString &iname, WatchItem *root) const; void reinitialize(); - void removeOutdated(); - void removeOutdatedHelper(WatchItem *item); - WatchItem *dummyRoot() const; + void removeItem(WatchItem *item); - void setActiveData(const QString &data) { m_activeData = data; } + WatchItem *root() const; + + WatchItem *dummyRoot() const; - void emitDataChanged(int column, - const QModelIndex &parentIndex = QModelIndex()); + void setValueByIName(const QString &iname, const QString &value); + void setValueByExpression(const QString &iname, const QString &value); + + void emitDataChanged(int column, const QModelIndex &parentIndex = QModelIndex()); + +protected: + WatchHandler *handler() const { return m_handler; } + QVariant data(const WatchData &data, int column, int role) const; + + static QString parentName(const QString &iname); private: - WatchHandler *m_handler; - WatchType m_type; WatchItem *m_root; - QString m_activeData; + WatchHandler *m_handler; + const WatchType m_type; +}; + +QDebug operator<<(QDebug d, const WatchModel &wm); + +/* A helper predicate for implementing model find routines */ +class WatchPredicate { +public: + enum Mode { INameMatch, ExpressionMatch }; + explicit WatchPredicate(Mode m, const QString &pattern); + bool operator()(const WatchData &w) const; + +private: + const QString &m_pattern; + const Mode m_mode; }; class WatchHandler : public QObject @@ -213,21 +259,25 @@ class WatchHandler : public QObject public: WatchHandler(); + void init(QTreeView *localsView); + WatchModel *model(WatchType type) const; + static WatchType watchTypeOfIName(const QString &iname); WatchModel *modelForIName(const QString &data) const; + QStringList watcherExpressions() const; + QString watcherName(const QString &exp); + + void setModel(WatchType type, WatchModel *model); + //public slots: void cleanup(); Q_SLOT void watchExpression(); // data in action->data().toString() Q_SLOT void watchExpression(const QString &exp); Q_SLOT void removeWatchExpression(); Q_SLOT void removeWatchExpression(const QString &exp); - void beginCycle(); // called at begin of updateLocals() cycle - void updateWatchers(); // called after locals are fetched - void endCycle(); // called after all results have been received void showEditValue(const WatchData &data); - void insertData(const WatchData &data); void removeData(const QString &iname); WatchData *findItem(const QString &iname) const; @@ -241,18 +291,32 @@ public: QSet<QString> expandedINames() const { return m_expandedINames; } + // For debuggers that clean out the locals models between frames: + // save/restore the expansion state. + Q_SLOT void restoreLocalsViewState(int frame = 0); + Q_SLOT void saveLocalsViewState(int frame = 0); + static QString watcherEditPlaceHolder(); signals: - void watchDataUpdateNeeded(const WatchData &data); + void watcherInserted(const WatchData &data); void sessionValueRequested(const QString &name, QVariant *value); void setSessionValueRequested(const QString &name, const QVariant &value); private: - friend class WatchModel; + friend class WatchModel; // needs formats, expanded inames + + // Per stack-frame locals state + struct LocalsViewState { + LocalsViewState() : firstVisibleRow(0) {} + int firstVisibleRow; + QSet<QString> expandedINames; + }; + typedef QMap<int, LocalsViewState> LocalsViewStateMap; void loadWatchers(); void saveWatchers(); + void initWatchModel(); void loadTypeFormats(); void saveTypeFormats(); @@ -265,17 +329,16 @@ private: EditWindows m_editWindows; QHash<QString, int> m_watcherNames; - QString watcherName(const QString &exp); QHash<QString, int> m_typeFormats; QHash<QString, int> m_individualFormats; void setDisplayedIName(const QString &iname, bool on); QSet<QString> m_expandedINames; // those expanded in the treeview QSet<QString> m_displayedINames; // those with "external" viewers + LocalsViewStateMap m_localsViewState; - WatchModel *m_locals; - WatchModel *m_watchers; - WatchModel *m_tooltips; + WatchModel *m_models[WatchModelCount]; + QPointer<QTreeView> m_localsView; }; } // namespace Internal diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp index 83486e899e9..7eccb04cd9e 100644 --- a/src/plugins/debugger/watchutils.cpp +++ b/src/plugins/debugger/watchutils.cpp @@ -554,19 +554,22 @@ QList<WatchData> QtDumperResult::toWatchData(int source) const } wchild.setType(dchild.type.isEmpty() ? childType : dchild.type); wchild.setAddress(dchild.address); - // Child overrides. - const int effectiveChildChildCount = dchild.childCount == -1 ? childChildCount : dchild.childCount; - switch (effectiveChildChildCount) { + // Child overrides. Note that WatchData::setType sets + // childcount = 0 for some known types. + if (wchild.isHasChildrenNeeded()) { + const int effectiveChildChildCount = dchild.childCount == -1 ? childChildCount : dchild.childCount; + switch (effectiveChildChildCount) { case -1: - wchild.setChildrenNeeded(); - wchild.setHasChildrenNeeded(); - break; + wchild.setChildrenNeeded(); + wchild.setHasChildrenNeeded(); + break; case 0: - wchild.setHasChildren(false); - break; + wchild.setHasChildren(false); + break; default: - wchild.setHasChildren(true); - break; + wchild.setHasChildren(true); + break; + } } } } diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp index 0df432ce179..73e9c2bf0b4 100644 --- a/src/plugins/debugger/watchwindow.cpp +++ b/src/plugins/debugger/watchwindow.cpp @@ -327,9 +327,11 @@ void WatchWindow::editItem(const QModelIndex &idx) Q_UNUSED(idx); // FIXME } -void WatchWindow::setModel(QAbstractItemModel *model) +void WatchWindow::setModel(QAbstractItemModel *newModel) { - QTreeView::setModel(model); + if (model() == newModel) + return; + QTreeView::setModel(newModel); setRootIsDecorated(true); header()->setDefaultAlignment(Qt::AlignLeft); @@ -337,7 +339,7 @@ void WatchWindow::setModel(QAbstractItemModel *model) if (m_type != LocalsType) header()->hide(); - connect(model, SIGNAL(layoutChanged()), this, SLOT(resetHelper())); + connect(newModel, SIGNAL(layoutChanged()), this, SLOT(resetHelper())); } void WatchWindow::resetHelper() -- GitLab