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>
This commit is contained in:
Friedemann Kleint
2009-07-10 14:36:28 +02:00
parent b0227ce496
commit abf5e3ddc3
29 changed files with 1798 additions and 697 deletions

View File

@@ -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

View File

@@ -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;
/* AbstractSyncWatchModel: To be used by synchonous debuggers.
* Implements all of WatchModel and provides new virtuals for
* the debugger to complete items. */
/* 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. */
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);
bool assignValue(const QString &iname, const QString &value,
QString *newValue /* = 0 */, QString *errorMessage);
bool editorToolTip(const QString &iname, QString *value, QString *errorMessage);
virtual QVariant data(const QModelIndex &index, int role) const;
bool populateModelInitially(WatchHandler *wh, QString *errorMessage);
virtual void fetchMore(const QModelIndex &parent);
virtual bool canFetchMore(const QModelIndex &parent) const;
bool completeData(const WatchData &incompleteLocal,
WatchHandler *wh,
QString *errorMessage);
signals:
// Emitted if one of fetchChildren/complete fails.
void error(const QString &);
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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
if (frameIndex < 0 || !m_d->m_currentStackTrace) {
errorMessage = tr("No current stack trace.");
break;
if (!sg->assignValue(expr, value, &newValue, &errorMessage))
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

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,

View File

@@ -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();
};

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
////////////////////////////////////////////////////////////////////
//
// WatchItem
//
////////////////////////////////////////////////////////////////////
WatchItem::WatchItem() :
parent(0),
fetchTriggered(false)
{
}
WatchItem::WatchItem(const WatchData &data) :
WatchData(data),
parent(0),
fetchTriggered(false)
{
}
void WatchItem::setData(const WatchData &data)
{
static_cast<WatchData &>(*this) = data;
}
WatchItem::~WatchItem()
{
qDeleteAll(children);
}
void WatchItem::removeChildren()
{
if (!children.empty()) {
qDeleteAll(children);
children.clear();
}
}
void WatchItem::addChild(WatchItem *child)
{
children.push_back(child);
child->parent = this;
}
///////////////////////////////////////////////////////////////////////
//
// WatchPredicate
//
///////////////////////////////////////////////////////////////////////
WatchPredicate::WatchPredicate(Mode mode, const QString &pattern) :
m_pattern(pattern),
m_mode(mode)
{
}
bool WatchPredicate::operator()(const WatchData &w) const
{
switch (m_mode) {
case INameMatch:
return m_pattern == w.iname;
case ExpressionMatch:
return m_pattern == w.exp;
}
return false;
}
///////////////////////////////////////////////////////////////////////
//
// WatchModel
//
///////////////////////////////////////////////////////////////////////
WatchModel::WatchModel(WatchHandler *handler, WatchType type)
: QAbstractItemModel(handler), m_handler(handler), m_type(type)
WatchModel::WatchModel(WatchHandler *handler, WatchType type, QObject *parent) :
QAbstractItemModel(parent),
m_root(new WatchItem),
m_handler(handler),
m_type(type)
{
m_root = new WatchItem;
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");
WatchItem *item = new WatchItem;
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;
m_root->children.append(item);
}
WatchItem *WatchModel::dummyRoot() const
WatchModel::~WatchModel()
{
QTC_ASSERT(!m_root->children.isEmpty(), return 0);
return m_root->children.front();
delete m_root;
}
void WatchModel::reinitialize()
QString WatchModel::parentName(const QString &iname)
{
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();
}
void WatchModel::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 WatchModel::removeOutdatedHelper(WatchItem *item)
{
if (item->generation < generationCounter)
removeItem(item);
else {
foreach (WatchItem *child, item->children)
removeOutdatedHelper(child);
item->fetchTriggered = false;
}
}
void WatchModel::removeItem(WatchItem *item)
{
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();
}
static QString parentName(const QString &iname)
{
int pos = iname.lastIndexOf(QLatin1Char('.'));
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
int WatchModel::columnCount(const QModelIndex &idx) 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
{
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
bool WatchModel::hasChildren(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
{
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();
}
static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
void WatchModel::setValueByIName(const QString &iname, 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 = 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));
}
}
return name1 < name2;
}
static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
void WatchModel::setValueByExpression(const QString &exp, const QString &value)
{
QList<WatchItem *>::const_iterator it =
qLowerBound(list.begin(), list.end(), item, iNameSorter);
return it - list.begin();
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));
}
}
}
void WatchModel::insertData(const WatchData &data)
WatchItem *WatchModel::root() const
{
// 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);
return m_root;
}
WatchItem *WatchModel::dummyRoot() const
{
if (!m_root->children.isEmpty())
return m_root->children.front();
return 0;
}
void WatchModel::reinitialize()
{
WatchItem *item = dummyRoot();
QTC_ASSERT(item, return);
const QModelIndex index = watchIndex(item);
const int n = item->children.size();
if (n == 0)
return;
}
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();
}
//MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << item->iname);
beginRemoveRows(index, 0, n - 1);
item->removeChildren();
endRemoveRows();
}
WatchItem *WatchModel::findItem(const QString &iname, WatchItem *root) const
void WatchModel::removeItem(WatchItem *itemIn)
{
if (root->iname == iname)
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;
}
return QModelIndex();
}
static WatchItem *findRecursion(WatchItem *root, const WatchPredicate &p)
{
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

View File

@@ -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);
virtual QModelIndex index(int, int, const QModelIndex &idx = QModelIndex()) const;
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);
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);
friend class WatchHandler;
friend class GdbEngine;
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; }
void emitDataChanged(int column,
const QModelIndex &parentIndex = QModelIndex());
void removeItem(WatchItem *item);
WatchItem *root() const;
WatchItem *dummyRoot() const;
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

View File

@@ -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;
}
}
}
}

View File

@@ -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()