forked from qt-creator/qt-creator
Debugger: Work on WatchModel performance
Don't instantiate repeating boilerplate item data in some cases (such as large arrays). This makes it necessary to access parent WatchItems in a lot more cases than before and needs another separation of WatchItem/WatchModel code to keep the dumper autotests in a functional state. For a plain std::vector<int> with 1 mio items this reduces extraction time from more than 2 minutes to about 3 seconds. Change-Id: I175c5f6ee90434a6e85342d8bb71bd10a04dd271 Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com> Reviewed-by: David Schulz <david.schulz@theqtcompany.com>
This commit is contained in:
@@ -354,7 +354,13 @@ class Dumper(DumperBase):
|
||||
def canCallLocale(self):
|
||||
return False if self.is32bit() else True
|
||||
|
||||
def reportTime(self, hint):
|
||||
#from datetime import datetime
|
||||
#warn("%s: %s" % (hint, datetime.now().time().isoformat()))
|
||||
pass
|
||||
|
||||
def fetchVariables(self, args):
|
||||
self.reportTime("begin fetch")
|
||||
self.prepare(args)
|
||||
partialVariable = args.get("partialvar", "")
|
||||
isPartial = len(partialVariable) > 0
|
||||
@@ -390,6 +396,8 @@ class Dumper(DumperBase):
|
||||
else:
|
||||
locals = self.listOfLocals()
|
||||
|
||||
self.reportTime("locals")
|
||||
|
||||
# Take care of the return value of the last function call.
|
||||
if len(self.resultVarName) > 0:
|
||||
try:
|
||||
@@ -439,7 +447,9 @@ class Dumper(DumperBase):
|
||||
|
||||
self.output.append(',partial="%d"' % isPartial)
|
||||
|
||||
self.reportTime("before print: %s" % len(self.output))
|
||||
safePrint(''.join(self.output))
|
||||
self.reportTime("after print")
|
||||
|
||||
def enterSubItem(self, item):
|
||||
if not item.iname:
|
||||
|
||||
@@ -807,7 +807,10 @@ def qdump__std__vector(d, value):
|
||||
base = d.pointerValue(start)
|
||||
for i in d.childRange():
|
||||
q = base + int(i / 8)
|
||||
d.putBoolItem(str(i), (int(d.extractPointer(q)) >> (i % 8)) & 1)
|
||||
with SubItem(d, i):
|
||||
d.putValue((int(d.extractPointer(q)) >> (i % 8)) & 1)
|
||||
d.putType("bool")
|
||||
d.putNumChild(0)
|
||||
else:
|
||||
d.putPlotData(start, size, type)
|
||||
|
||||
@@ -840,7 +843,10 @@ def qdump__std__vector__QNX(d, value):
|
||||
with Children(d, size, maxNumChild=10000, childType=innerType):
|
||||
for i in d.childRange():
|
||||
q = start + int(i / storagesize)
|
||||
d.putBoolItem(str(i), (q.dereference() >> (i % storagesize)) & 1)
|
||||
with SubItem(d, i):
|
||||
d.putValue((q.dereference() >> (i % storagesize)) & 1)
|
||||
d.putType("bool")
|
||||
d.putNumChild(0)
|
||||
else:
|
||||
d.putArrayData(start, size, innerType)
|
||||
|
||||
|
||||
@@ -1982,9 +1982,10 @@ void DebuggerEngine::updateLocalsView(const GdbMi &all)
|
||||
|
||||
GdbMi data = all["data"];
|
||||
foreach (const GdbMi &child, data.children()) {
|
||||
WatchItem *item = new WatchItem(child);
|
||||
WatchItem *item = new WatchItem;
|
||||
item->parse(child);
|
||||
const TypeInfo ti = d->m_typeInfoCache.value(item->type);
|
||||
if (ti.size)
|
||||
if (ti.size && !item->size)
|
||||
item->size = ti.size;
|
||||
|
||||
handler->insertItem(item);
|
||||
|
||||
@@ -63,7 +63,6 @@ class DebuggerEnginePrivate;
|
||||
class DebuggerPluginPrivate;
|
||||
class DisassemblerAgent;
|
||||
class MemoryAgent;
|
||||
class WatchData;
|
||||
class WatchItem;
|
||||
class BreakHandler;
|
||||
class LocationMark;
|
||||
|
||||
@@ -1036,7 +1036,6 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(DebuggerPlugin *plugin) :
|
||||
m_dummyEngine(0),
|
||||
m_globalDebuggerOptions(new GlobalDebuggerOptions)
|
||||
{
|
||||
qRegisterMetaType<WatchData>("WatchData");
|
||||
qRegisterMetaType<ContextData>("ContextData");
|
||||
qRegisterMetaType<DebuggerRunParameters>("DebuggerRunParameters");
|
||||
|
||||
|
||||
@@ -193,11 +193,11 @@ void DraggableLabel::mouseMoveEvent(QMouseEvent * event)
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ToolTipWatchItem : public Utils::TreeItem
|
||||
class ToolTipWatchItem : public TreeItem
|
||||
{
|
||||
public:
|
||||
ToolTipWatchItem() : expandable(false) {}
|
||||
ToolTipWatchItem(WatchItem *item);
|
||||
ToolTipWatchItem(TreeItem *item);
|
||||
|
||||
bool hasChildren() const { return expandable; }
|
||||
bool canFetchMore() const { return childCount() == 0 && expandable && model(); }
|
||||
@@ -214,17 +214,19 @@ public:
|
||||
QByteArray iname;
|
||||
};
|
||||
|
||||
ToolTipWatchItem::ToolTipWatchItem(WatchItem *item)
|
||||
ToolTipWatchItem::ToolTipWatchItem(TreeItem *item)
|
||||
{
|
||||
name = item->displayName();
|
||||
value = item->displayValue();
|
||||
type = item->displayType();
|
||||
iname = item->iname;
|
||||
valueColor = item->valueColor(1);
|
||||
const TreeModel *model = item->model();
|
||||
QModelIndex idx = model->indexForItem(item);
|
||||
name = model->data(idx.sibling(idx.row(), 0), Qt::DisplayRole).toString();
|
||||
value = model->data(idx.sibling(idx.row(), 1), Qt::DisplayRole).toString();
|
||||
type = model->data(idx.sibling(idx.row(), 2), Qt::DisplayRole).toString();
|
||||
iname = model->data(idx.sibling(idx.row(), 0), LocalsINameRole).toByteArray();
|
||||
valueColor = model->data(idx.sibling(idx.row(), 1), Qt::ForegroundRole).value<QColor>();
|
||||
expandable = item->hasChildren();
|
||||
expression = item->expression();
|
||||
expression = model->data(idx.sibling(idx.row(), 0), Qt::EditRole).toString();
|
||||
foreach (TreeItem *child, item->children())
|
||||
appendChild(new ToolTipWatchItem(static_cast<WatchItem *>(child)));
|
||||
appendChild(new ToolTipWatchItem(child));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
@@ -1175,7 +1177,7 @@ static void slotTooltipOverrideRequested
|
||||
purgeClosedToolTips();
|
||||
|
||||
// Prefer a filter on an existing local variable if it can be found.
|
||||
const WatchData *localVariable = engine->watchHandler()->findCppLocalVariable(context.expression);
|
||||
const WatchItem *localVariable = engine->watchHandler()->findCppLocalVariable(context.expression);
|
||||
if (localVariable) {
|
||||
context.expression = QLatin1String(localVariable->exp);
|
||||
if (context.expression.isEmpty())
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class WatchData;
|
||||
class GdbMi;
|
||||
|
||||
/* A debugger engine interfacing the LLDB debugger
|
||||
|
||||
@@ -518,8 +518,11 @@ void PdbEngine::refreshLocals(const GdbMi &vars)
|
||||
WatchHandler *handler = watchHandler();
|
||||
handler->resetValueCache();
|
||||
|
||||
foreach (const GdbMi &child, vars.children())
|
||||
handler->insertItem(new WatchItem(child));
|
||||
foreach (const GdbMi &child, vars.children()) {
|
||||
WatchItem *item = new WatchItem;
|
||||
item->parse(child);
|
||||
handler->insertItem(item);
|
||||
}
|
||||
|
||||
handler->notifyUpdateFinished();
|
||||
|
||||
|
||||
@@ -1089,11 +1089,11 @@ void QmlEngine::updateCurrentContext()
|
||||
context = stackHandler()->currentFrame().function;
|
||||
} else {
|
||||
QModelIndex currentIndex = inspectorView()->currentIndex();
|
||||
const WatchData *currentData = watchHandler()->watchItem(currentIndex);
|
||||
const WatchItem *currentData = watchHandler()->watchItem(currentIndex);
|
||||
if (!currentData)
|
||||
return;
|
||||
const WatchData *parentData = watchHandler()->watchItem(currentIndex.parent());
|
||||
const WatchData *grandParentData = watchHandler()->watchItem(currentIndex.parent().parent());
|
||||
const WatchItem *parentData = watchHandler()->watchItem(currentIndex.parent());
|
||||
const WatchItem *grandParentData = watchHandler()->watchItem(currentIndex.parent().parent());
|
||||
if (currentData->id != parentData->id)
|
||||
context = currentData->name;
|
||||
else if (parentData->id != grandParentData->id)
|
||||
@@ -1351,7 +1351,9 @@ void QmlEnginePrivate::handleEvaluateExpression(const QVariantMap &response,
|
||||
QmlV8ObjectData body = extractData(bodyVal);
|
||||
WatchHandler *watchHandler = engine->watchHandler();
|
||||
|
||||
auto item = new WatchItem(iname, exp);
|
||||
auto item = new WatchItem;
|
||||
item->iname = iname;
|
||||
item->name = exp;
|
||||
item->exp = exp.toLatin1();
|
||||
item->id = body.handle;
|
||||
bool success = response.value(_("success")).toBool();
|
||||
@@ -2170,9 +2172,11 @@ void QmlEnginePrivate::handleFrame(const QVariantMap &response)
|
||||
{
|
||||
QByteArray iname = "local.this";
|
||||
QString exp = QLatin1String("this");
|
||||
|
||||
auto item = new WatchItem(iname, exp);
|
||||
QmlV8ObjectData objectData = extractData(body.value(_("receiver")));
|
||||
|
||||
auto item = new WatchItem;
|
||||
item->iname = iname;
|
||||
item->name = exp;
|
||||
item->id = objectData.handle;
|
||||
item->type = objectData.type;
|
||||
item->value = objectData.value.toString();
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class WatchData;
|
||||
class WatchItem;
|
||||
class QmlEnginePrivate;
|
||||
class QmlInspectorAgent;
|
||||
|
||||
@@ -74,12 +74,12 @@ QmlInspectorAgent::QmlInspectorAgent(QmlEngine *engine, QmlDebugConnection *conn
|
||||
, m_engineClient(0)
|
||||
, m_engineQueryId(0)
|
||||
, m_rootContextQueryId(0)
|
||||
, m_objectToSelect(WatchData::InvalidId)
|
||||
, m_objectToSelect(WatchItem::InvalidId)
|
||||
, m_masterEngine(engine)
|
||||
, m_toolsClient(0)
|
||||
, m_targetToSync(NoTarget)
|
||||
, m_debugIdToSelect(WatchData::InvalidId)
|
||||
, m_currentSelectedDebugId(WatchData::InvalidId)
|
||||
, m_debugIdToSelect(WatchItem::InvalidId)
|
||||
, m_currentSelectedDebugId(WatchItem::InvalidId)
|
||||
, m_toolsClientConnected(false)
|
||||
, m_inspectorToolsContext("Debugger.QmlInspector")
|
||||
, m_selectAction(new QAction(this))
|
||||
@@ -87,7 +87,7 @@ QmlInspectorAgent::QmlInspectorAgent(QmlEngine *engine, QmlDebugConnection *conn
|
||||
, m_showAppOnTopAction(action(ShowAppOnTop))
|
||||
, m_engineClientConnected(false)
|
||||
{
|
||||
m_debugIdToIname.insert(WatchData::InvalidId, QByteArray("inspect"));
|
||||
m_debugIdToIname.insert(WatchItem::InvalidId, QByteArray("inspect"));
|
||||
connect(action(ShowQmlObjectTree),
|
||||
&Utils::SavedAction::valueChanged, this, &QmlInspectorAgent::updateState);
|
||||
m_delayQueryTimer.setSingleShot(true);
|
||||
@@ -173,13 +173,13 @@ quint32 QmlInspectorAgent::queryExpressionResult(int debugId,
|
||||
m_engine.debugId());
|
||||
}
|
||||
|
||||
void QmlInspectorAgent::assignValue(const WatchData *data,
|
||||
void QmlInspectorAgent::assignValue(const WatchItem *data,
|
||||
const QString &expr, const QVariant &valueV)
|
||||
{
|
||||
qCDebug(qmlInspectorLog)
|
||||
<< __FUNCTION__ << '(' << data->id << ')' << data->iname;
|
||||
|
||||
if (data->id != WatchData::InvalidId) {
|
||||
if (data->id != WatchItem::InvalidId) {
|
||||
QString val(valueV.toString());
|
||||
if (valueV.type() == QVariant::String) {
|
||||
val = val.replace(QLatin1Char('\"'), QLatin1String("\\\""));
|
||||
@@ -195,17 +195,17 @@ static int parentIdForIname(const QByteArray &iname)
|
||||
// Extract the parent id
|
||||
int lastIndex = iname.lastIndexOf('.');
|
||||
int secondLastIndex = iname.lastIndexOf('.', lastIndex - 1);
|
||||
int parentId = WatchData::InvalidId;
|
||||
if (secondLastIndex != WatchData::InvalidId)
|
||||
int parentId = WatchItem::InvalidId;
|
||||
if (secondLastIndex != WatchItem::InvalidId)
|
||||
parentId = iname.mid(secondLastIndex + 1, lastIndex - secondLastIndex - 1).toInt();
|
||||
return parentId;
|
||||
}
|
||||
|
||||
void QmlInspectorAgent::updateWatchData(const WatchData &data)
|
||||
void QmlInspectorAgent::updateWatchData(const WatchItem &data)
|
||||
{
|
||||
qCDebug(qmlInspectorLog) << __FUNCTION__ << '(' << data.id << ')';
|
||||
|
||||
if (data.id != WatchData::InvalidId && !m_fetchDataIds.contains(data.id)) {
|
||||
if (data.id != WatchItem::InvalidId && !m_fetchDataIds.contains(data.id)) {
|
||||
// objects
|
||||
m_fetchDataIds << data.id;
|
||||
fetchObject(data.id);
|
||||
@@ -216,7 +216,7 @@ void QmlInspectorAgent::watchDataSelected(qint64 id)
|
||||
{
|
||||
qCDebug(qmlInspectorLog) << __FUNCTION__ << '(' << id << ')';
|
||||
|
||||
if (id != WatchData::InvalidId) {
|
||||
if (id != WatchItem::InvalidId) {
|
||||
QTC_ASSERT(m_debugIdLocations.keys().contains(id), return);
|
||||
jumpToObjectDefinitionInEditor(m_debugIdLocations.value(id), id);
|
||||
if (m_toolsClient)
|
||||
@@ -278,7 +278,7 @@ ObjectReference QmlInspectorAgent::objectForId(int objectDebugId) const
|
||||
}
|
||||
}
|
||||
// TODO: Set correct parentId
|
||||
return ObjectReference(objectDebugId, WatchData::InvalidId,
|
||||
return ObjectReference(objectDebugId, WatchItem::InvalidId,
|
||||
FileReference(QUrl::fromLocalFile(file), line, column));
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ void QmlInspectorAgent::addObjectWatch(int objectDebugId)
|
||||
{
|
||||
qCDebug(qmlInspectorLog) << __FUNCTION__ << '(' << objectDebugId << ')';
|
||||
|
||||
if (objectDebugId == WatchData::InvalidId)
|
||||
if (objectDebugId == WatchItem::InvalidId)
|
||||
return;
|
||||
|
||||
if (!isConnected() || !boolSetting(ShowQmlObjectTree))
|
||||
@@ -470,7 +470,7 @@ void QmlInspectorAgent::verifyAndInsertObjectInTree(const ObjectReference &objec
|
||||
|
||||
// Find out the correct position in the tree
|
||||
// Objects are inserted to the tree if they satisfy one of the two conditions.
|
||||
// Condition 1: Object is a root object i.e. parentId == WatchData::InvalidId.
|
||||
// Condition 1: Object is a root object i.e. parentId == WatchItem::InvalidId.
|
||||
// Condition 2: Object has an expanded parent i.e. siblings are known.
|
||||
// If the two conditions are not met then we push the object to a stack and recursively
|
||||
// fetch parents till we find a previously expanded parent.
|
||||
@@ -480,7 +480,7 @@ void QmlInspectorAgent::verifyAndInsertObjectInTree(const ObjectReference &objec
|
||||
const int objectDebugId = object.debugId();
|
||||
if (m_debugIdToIname.contains(parentId)) {
|
||||
QByteArray parentIname = m_debugIdToIname.value(parentId);
|
||||
if (parentId != WatchData::InvalidId && !handler->isExpandedIName(parentIname)) {
|
||||
if (parentId != WatchItem::InvalidId && !handler->isExpandedIName(parentIname)) {
|
||||
m_objectStack.push(object);
|
||||
handler->fetchMore(parentIname);
|
||||
return; // recursive
|
||||
@@ -535,7 +535,7 @@ void QmlInspectorAgent::insertObjectInTree(const ObjectReference &object)
|
||||
<< timeElapsed.elapsed() << " ms";
|
||||
|
||||
if (object.debugId() == m_debugIdToSelect) {
|
||||
m_debugIdToSelect = WatchData::InvalidId;
|
||||
m_debugIdToSelect = WatchItem::InvalidId;
|
||||
selectObject(object, m_targetToSync);
|
||||
}
|
||||
|
||||
@@ -544,7 +544,7 @@ void QmlInspectorAgent::insertObjectInTree(const ObjectReference &object)
|
||||
QByteArray iname = m_debugIdToIname.value(m_objectToSelect);
|
||||
qCDebug(qmlInspectorLog) << " selecting" << iname << "in tree";
|
||||
m_qmlEngine->watchHandler()->setCurrentItem(iname);
|
||||
m_objectToSelect = WatchData::InvalidId;
|
||||
m_objectToSelect = WatchItem::InvalidId;
|
||||
}
|
||||
m_qmlEngine->watchHandler()->updateWatchersWindow();
|
||||
m_qmlEngine->watchHandler()->reexpandItems();
|
||||
@@ -613,7 +613,9 @@ void QmlInspectorAgent::addWatchData(const ObjectReference &obj,
|
||||
return;
|
||||
|
||||
// object
|
||||
auto objWatch = new WatchItem(objIname, name);
|
||||
auto objWatch = new WatchItem;
|
||||
objWatch->iname = objIname;
|
||||
objWatch->name = name;
|
||||
objWatch->id = objDebugId;
|
||||
objWatch->exp = name.toLatin1();
|
||||
objWatch->type = obj.className().toLatin1();
|
||||
@@ -643,7 +645,9 @@ void QmlInspectorAgent::addWatchData(const ObjectReference &obj,
|
||||
// properties
|
||||
if (append && obj.properties().count()) {
|
||||
QByteArray iname = objIname + ".[properties]";
|
||||
auto propertiesWatch = new WatchItem(iname, tr("Properties"));
|
||||
auto propertiesWatch = new WatchItem;
|
||||
propertiesWatch->iname = iname;
|
||||
propertiesWatch->name = tr("Properties");
|
||||
propertiesWatch->id = objDebugId;
|
||||
propertiesWatch->value = _("list");
|
||||
propertiesWatch->wantsChildren = true;
|
||||
@@ -653,7 +657,9 @@ void QmlInspectorAgent::addWatchData(const ObjectReference &obj,
|
||||
const QString propertyName = property.name();
|
||||
if (propertyName.isEmpty())
|
||||
continue;
|
||||
auto propertyWatch = new WatchItem(buildIName(iname, propertyName), propertyName);
|
||||
auto propertyWatch = new WatchItem;
|
||||
propertyWatch->iname = buildIName(iname, propertyName);
|
||||
propertyWatch->name = propertyName;
|
||||
propertyWatch->id = objDebugId;
|
||||
propertyWatch->exp = propertyName.toLatin1();
|
||||
propertyWatch->type = property.valueTypeName().toLatin1();
|
||||
@@ -700,7 +706,7 @@ void QmlInspectorAgent::clearObjectTree()
|
||||
m_debugIdHash.clear();
|
||||
m_debugIdHash.reserve(old_count + 1);
|
||||
m_debugIdToIname.clear();
|
||||
m_debugIdToIname.insert(WatchData::InvalidId, QByteArray("inspect"));
|
||||
m_debugIdToIname.insert(WatchItem::InvalidId, QByteArray("inspect"));
|
||||
m_objectStack.clear();
|
||||
m_objectWatches.clear();
|
||||
}
|
||||
@@ -870,7 +876,7 @@ void QmlInspectorAgent::jumpToObjectDefinitionInEditor(
|
||||
const QString fileName = m_masterEngine->toFileInProject(objSource.url());
|
||||
|
||||
Core::EditorManager::openEditorAt(fileName, objSource.lineNumber());
|
||||
if (debugId != WatchData::InvalidId && debugId != m_currentSelectedDebugId) {
|
||||
if (debugId != WatchItem::InvalidId && debugId != m_currentSelectedDebugId) {
|
||||
m_currentSelectedDebugId = debugId;
|
||||
m_currentSelectedDebugName = displayName(debugId);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Internal {
|
||||
|
||||
class DebuggerEngine;
|
||||
class QmlEngine;
|
||||
class WatchData;
|
||||
class WatchItem;
|
||||
|
||||
//map <filename, editorRevision> -> <lineNumber, columnNumber> -> debugId
|
||||
typedef
|
||||
@@ -67,8 +67,8 @@ public:
|
||||
void fetchObject(int debugId);
|
||||
quint32 queryExpressionResult(int debugId, const QString &expression);
|
||||
|
||||
void assignValue(const WatchData *data, const QString &expression, const QVariant &valueV);
|
||||
void updateWatchData(const WatchData &data);
|
||||
void assignValue(const WatchItem *data, const QString &expression, const QVariant &valueV);
|
||||
void updateWatchData(const WatchItem &data);
|
||||
void watchDataSelected(qint64 id);
|
||||
bool selectObjectInTree(int debugId);
|
||||
void addObjectWatch(int objectDebugId);
|
||||
|
||||
@@ -179,7 +179,7 @@ static void blockRecursion(const Overview &overview,
|
||||
// Is the declaration on or past the current line, that is,
|
||||
// the variable not initialized.
|
||||
if (symbol->line() >= line)
|
||||
uninitializedVariables->push_back(WatchData::shadowedName(name, it.value()));
|
||||
uninitializedVariables->push_back(WatchItem::shadowedName(name, it.value()));
|
||||
}
|
||||
}
|
||||
// Next block scope.
|
||||
|
||||
@@ -37,12 +37,6 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WatchData
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
@@ -118,8 +112,8 @@ bool isIntOrFloatType(const QByteArray &type)
|
||||
return isIntType(type) || isFloatType(type);
|
||||
}
|
||||
|
||||
WatchData::WatchData() :
|
||||
id(WatchData::InvalidId),
|
||||
WatchItem::WatchItem() :
|
||||
id(WatchItem::InvalidId),
|
||||
state(InitialState),
|
||||
editformat(StopDisplay),
|
||||
address(0),
|
||||
@@ -128,6 +122,7 @@ WatchData::WatchData() :
|
||||
bitpos(0),
|
||||
bitsize(0),
|
||||
elided(0),
|
||||
arrayIndex(-1),
|
||||
wantsChildren(false),
|
||||
valueEnabled(true),
|
||||
valueEditable(true),
|
||||
@@ -135,16 +130,7 @@ WatchData::WatchData() :
|
||||
{
|
||||
}
|
||||
|
||||
bool WatchData::isAncestorOf(const QByteArray &childIName) const
|
||||
{
|
||||
if (iname.size() >= childIName.size())
|
||||
return false;
|
||||
if (!childIName.startsWith(iname))
|
||||
return false;
|
||||
return childIName.at(iname.size()) == '.';
|
||||
}
|
||||
|
||||
bool WatchData::isVTablePointer() const
|
||||
bool WatchItem::isVTablePointer() const
|
||||
{
|
||||
// First case: Cdb only. No user type can be named like this, this is safe.
|
||||
// Second case: Python dumper only.
|
||||
@@ -152,7 +138,7 @@ bool WatchData::isVTablePointer() const
|
||||
|| (type.isEmpty() && name == QLatin1String("[vptr]"));
|
||||
}
|
||||
|
||||
void WatchData::setError(const QString &msg)
|
||||
void WatchItem::setError(const QString &msg)
|
||||
{
|
||||
setAllUnneeded();
|
||||
value = msg;
|
||||
@@ -161,7 +147,7 @@ void WatchData::setError(const QString &msg)
|
||||
valueEditable = false;
|
||||
}
|
||||
|
||||
void WatchData::setValue(const QString &value0)
|
||||
void WatchItem::setValue(const QString &value0)
|
||||
{
|
||||
value = value0;
|
||||
if (value == QLatin1String("{...}")) {
|
||||
@@ -216,7 +202,7 @@ static GuessChildrenResult guessChildren(const QByteArray &type)
|
||||
return HasPossiblyChildren;
|
||||
}
|
||||
|
||||
void WatchData::setType(const QByteArray &str, bool guessChildrenFromType)
|
||||
void WatchItem::setType(const QByteArray &str, bool guessChildrenFromType)
|
||||
{
|
||||
type = str.trimmed();
|
||||
bool changed = true;
|
||||
@@ -253,7 +239,7 @@ void WatchData::setType(const QByteArray &str, bool guessChildrenFromType)
|
||||
}
|
||||
}
|
||||
|
||||
QString WatchData::toString() const
|
||||
QString WatchItem::toString() const
|
||||
{
|
||||
const char *doubleQuoteComma = "\",";
|
||||
QString res;
|
||||
@@ -299,6 +285,254 @@ QString WatchData::toString() const
|
||||
return res + QLatin1Char('}');
|
||||
}
|
||||
|
||||
QString WatchItem::msgNotInScope()
|
||||
{
|
||||
//: Value of variable in Debugger Locals display for variables out
|
||||
//: of scope (stopped above initialization).
|
||||
static const QString rc =
|
||||
QCoreApplication::translate("Debugger::Internal::WatchItem", "<not in scope>");
|
||||
return rc;
|
||||
}
|
||||
|
||||
const QString &WatchItem::shadowedNameFormat()
|
||||
{
|
||||
//: Display of variables shadowed by variables of the same name
|
||||
//: in nested scopes: Variable %1 is the variable name, %2 is a
|
||||
//: simple count.
|
||||
static const QString format =
|
||||
QCoreApplication::translate("Debugger::Internal::WatchItem", "%1 <shadowed %2>");
|
||||
return format;
|
||||
}
|
||||
|
||||
QString WatchItem::shadowedName(const QString &name, int seen)
|
||||
{
|
||||
if (seen <= 0)
|
||||
return name;
|
||||
return shadowedNameFormat().arg(name).arg(seen);
|
||||
}
|
||||
|
||||
QByteArray WatchItem::hexAddress() const
|
||||
{
|
||||
if (address)
|
||||
return QByteArray("0x") + QByteArray::number(address, 16);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QString decodeItemHelper(const T &t)
|
||||
{
|
||||
return QString::number(t);
|
||||
}
|
||||
|
||||
QString decodeItemHelper(const double &t)
|
||||
{
|
||||
return QString::number(t, 'g', 16);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void decodeArrayHelper(WatchItem *item, const QByteArray &rawData, int size, const QByteArray &childType)
|
||||
{
|
||||
const QByteArray ba = QByteArray::fromHex(rawData);
|
||||
const T *p = (const T *) ba.data();
|
||||
for (int i = 0, n = ba.size() / sizeof(T); i < n; ++i) {
|
||||
WatchItem *child = new WatchItem;
|
||||
child->arrayIndex = i;
|
||||
child->value = decodeItemHelper(p[i]);
|
||||
child->size = size;
|
||||
child->type = childType;
|
||||
child->setAllUnneeded();
|
||||
item->appendChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
static void decodeArrayData(WatchItem *item, const QByteArray &rawData,
|
||||
const DebuggerEncoding &encoding, const QByteArray &childType)
|
||||
{
|
||||
switch (encoding.type) {
|
||||
case DebuggerEncoding::HexEncodedSignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
return decodeArrayHelper<signed char>(item, rawData, encoding.size, childType);
|
||||
case 2:
|
||||
return decodeArrayHelper<short>(item, rawData, encoding.size, childType);
|
||||
case 4:
|
||||
return decodeArrayHelper<int>(item, rawData, encoding.size, childType);
|
||||
case 8:
|
||||
return decodeArrayHelper<qint64>(item, rawData, encoding.size, childType);
|
||||
}
|
||||
case DebuggerEncoding::HexEncodedUnsignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
return decodeArrayHelper<uchar>(item, rawData, encoding.size, childType);
|
||||
case 2:
|
||||
return decodeArrayHelper<ushort>(item, rawData, encoding.size, childType);
|
||||
case 4:
|
||||
return decodeArrayHelper<uint>(item, rawData, encoding.size, childType);
|
||||
case 8:
|
||||
return decodeArrayHelper<quint64>(item, rawData, encoding.size, childType);
|
||||
}
|
||||
break;
|
||||
case DebuggerEncoding::HexEncodedFloat:
|
||||
switch (encoding.size) {
|
||||
case 4:
|
||||
return decodeArrayHelper<float>(item, rawData, encoding.size, childType);
|
||||
case 8:
|
||||
return decodeArrayHelper<double>(item, rawData, encoding.size, childType);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qDebug() << "ENCODING ERROR: " << encoding.toString();
|
||||
}
|
||||
|
||||
void WatchItem::parseHelper(const GdbMi &input)
|
||||
{
|
||||
setChildrenUnneeded();
|
||||
|
||||
GdbMi mi = input["type"];
|
||||
if (mi.isValid())
|
||||
setType(mi.data());
|
||||
|
||||
editvalue = input["editvalue"].data();
|
||||
editformat = DebuggerDisplay(input["editformat"].toInt());
|
||||
editencoding = DebuggerEncoding(input["editencoding"].data());
|
||||
|
||||
mi = input["valueelided"];
|
||||
if (mi.isValid())
|
||||
elided = mi.toInt();
|
||||
|
||||
mi = input["bitpos"];
|
||||
if (mi.isValid())
|
||||
bitpos = mi.toInt();
|
||||
|
||||
mi = input["bitsize"];
|
||||
if (mi.isValid())
|
||||
bitsize = mi.toInt();
|
||||
|
||||
mi = input["origaddr"];
|
||||
if (mi.isValid())
|
||||
origaddr = mi.toAddress();
|
||||
|
||||
mi = input["address"];
|
||||
if (mi.isValid()) {
|
||||
address = mi.toAddress();
|
||||
if (exp.isEmpty()) {
|
||||
if (iname.startsWith("local.") && iname.count('.') == 1)
|
||||
// Solve one common case of adding 'class' in
|
||||
// *(class X*)0xdeadbeef for gdb.
|
||||
exp = name.toLatin1();
|
||||
else
|
||||
exp = "*(" + gdbQuoteTypes(type) + "*)" + hexAddress();
|
||||
}
|
||||
}
|
||||
|
||||
mi = input["value"];
|
||||
QByteArray enc = input["valueencoded"].data();
|
||||
if (mi.isValid() || !enc.isEmpty()) {
|
||||
setValue(decodeData(mi.data(), enc));
|
||||
} else {
|
||||
setValueNeeded();
|
||||
}
|
||||
|
||||
mi = input["size"];
|
||||
if (mi.isValid())
|
||||
size = mi.toInt();
|
||||
|
||||
mi = input["exp"];
|
||||
if (mi.isValid())
|
||||
exp = mi.data();
|
||||
|
||||
mi = input["valueenabled"];
|
||||
if (mi.data() == "true")
|
||||
valueEnabled = true;
|
||||
else if (mi.data() == "false")
|
||||
valueEnabled = false;
|
||||
|
||||
mi = input["valueeditable"];
|
||||
if (mi.data() == "true")
|
||||
valueEditable = true;
|
||||
else if (mi.data() == "false")
|
||||
valueEditable = false;
|
||||
|
||||
mi = input["numchild"]; // GDB/MI
|
||||
if (mi.isValid())
|
||||
setHasChildren(mi.toInt() > 0);
|
||||
mi = input["haschild"]; // native-mixed
|
||||
if (mi.isValid())
|
||||
setHasChildren(mi.toInt() > 0);
|
||||
|
||||
mi = input["arraydata"];
|
||||
if (mi.isValid()) {
|
||||
DebuggerEncoding encoding(input["arrayencoding"].data());
|
||||
QByteArray childType = input["childtype"].data();
|
||||
decodeArrayData(this, mi.data(), encoding, childType);
|
||||
} else {
|
||||
const GdbMi children = input["children"];
|
||||
if (children.isValid()) {
|
||||
bool ok = false;
|
||||
// Try not to repeat data too often.
|
||||
const GdbMi childType = input["childtype"];
|
||||
const GdbMi childNumChild = input["childnumchild"];
|
||||
|
||||
qulonglong addressBase = input["addrbase"].data().toULongLong(&ok, 0);
|
||||
qulonglong addressStep = input["addrstep"].data().toULongLong(&ok, 0);
|
||||
|
||||
for (int i = 0, n = int(children.children().size()); i != n; ++i) {
|
||||
const GdbMi &subinput = children.children().at(i);
|
||||
WatchItem *child = new WatchItem;
|
||||
if (childType.isValid())
|
||||
child->setType(childType.data());
|
||||
if (childNumChild.isValid())
|
||||
child->setHasChildren(childNumChild.toInt() > 0);
|
||||
GdbMi name = subinput["name"];
|
||||
QByteArray nn;
|
||||
if (name.isValid()) {
|
||||
nn = name.data();
|
||||
child->name = QString::fromLatin1(nn);
|
||||
} else {
|
||||
nn.setNum(i);
|
||||
child->name = QString::fromLatin1("[%1]").arg(i);
|
||||
}
|
||||
GdbMi iname = subinput["iname"];
|
||||
if (iname.isValid())
|
||||
child->iname = iname.data();
|
||||
else
|
||||
child->iname = this->iname + '.' + nn;
|
||||
if (addressStep) {
|
||||
child->address = addressBase + i * addressStep;
|
||||
child->exp = "*(" + gdbQuoteTypes(child->type) + "*)" + child->hexAddress();
|
||||
}
|
||||
QByteArray key = subinput["key"].data();
|
||||
if (!key.isEmpty())
|
||||
child->name = decodeData(key, subinput["keyencoded"].data());
|
||||
child->parseHelper(subinput);
|
||||
appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WatchItem::parse(const GdbMi &data)
|
||||
{
|
||||
iname = data["iname"].data();
|
||||
|
||||
GdbMi wname = data["wname"];
|
||||
if (wname.isValid()) // Happens (only) for watched expressions.
|
||||
name = QString::fromUtf8(QByteArray::fromHex(wname.data()));
|
||||
else
|
||||
name = QString::fromLatin1(data["name"].data());
|
||||
|
||||
parseHelper(data);
|
||||
|
||||
if (wname.isValid())
|
||||
exp = name.toUtf8();
|
||||
}
|
||||
|
||||
WatchItem *WatchItem::parentItem() const
|
||||
{
|
||||
return static_cast<WatchItem *>(parent());
|
||||
}
|
||||
|
||||
// Format a tooltip row with aligned colon.
|
||||
static void formatToolTipRow(QTextStream &str, const QString &category, const QString &value)
|
||||
{
|
||||
@@ -310,13 +544,13 @@ static void formatToolTipRow(QTextStream &str, const QString &category, const QS
|
||||
str << "</td><td>" << val << "</td></tr>";
|
||||
}
|
||||
|
||||
QString WatchData::toToolTip() const
|
||||
QString WatchItem::toToolTip() const
|
||||
{
|
||||
QString res;
|
||||
QTextStream str(&res);
|
||||
str << "<html><body><table>";
|
||||
formatToolTipRow(str, tr("Name"), name);
|
||||
formatToolTipRow(str, tr("Expression"), QLatin1String(exp));
|
||||
formatToolTipRow(str, tr("Expression"), expression());
|
||||
formatToolTipRow(str, tr("Internal Type"), QLatin1String(type));
|
||||
bool ok;
|
||||
const quint64 intValue = value.toULongLong(&ok);
|
||||
@@ -334,367 +568,92 @@ QString WatchData::toToolTip() const
|
||||
}
|
||||
formatToolTipRow(str, tr("Value"), val);
|
||||
}
|
||||
if (address)
|
||||
formatToolTipRow(str, tr("Object Address"), formatToolTipAddress(address));
|
||||
if (realAddress())
|
||||
formatToolTipRow(str, tr("Object Address"), formatToolTipAddress(realAddress()));
|
||||
if (origaddr)
|
||||
formatToolTipRow(str, tr("Pointer Address"), formatToolTipAddress(origaddr));
|
||||
if (arrayIndex >= 0)
|
||||
formatToolTipRow(str, tr("Array Index"), QString::number(arrayIndex));
|
||||
if (size)
|
||||
formatToolTipRow(str, tr("Static Object Size"), tr("%n bytes", 0, size));
|
||||
formatToolTipRow(str, tr("Internal ID"), QLatin1String(iname));
|
||||
formatToolTipRow(str, tr("Internal ID"), QLatin1String(internalName()));
|
||||
str << "</table></body></html>";
|
||||
return res;
|
||||
}
|
||||
|
||||
QString WatchData::msgNotInScope()
|
||||
bool WatchItem::isLocal() const
|
||||
{
|
||||
//: Value of variable in Debugger Locals display for variables out
|
||||
//: of scope (stopped above initialization).
|
||||
static const QString rc =
|
||||
QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
|
||||
return rc;
|
||||
if (arrayIndex >= 0)
|
||||
if (const WatchItem *p = parentItem())
|
||||
return p->isLocal();
|
||||
return iname.startsWith("local.");
|
||||
}
|
||||
|
||||
const QString &WatchData::shadowedNameFormat()
|
||||
bool WatchItem::isWatcher() const
|
||||
{
|
||||
//: Display of variables shadowed by variables of the same name
|
||||
//: in nested scopes: Variable %1 is the variable name, %2 is a
|
||||
//: simple count.
|
||||
static const QString format =
|
||||
QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>");
|
||||
return format;
|
||||
if (arrayIndex >= 0)
|
||||
if (const WatchItem *p = parentItem())
|
||||
return p->isWatcher();
|
||||
return iname.startsWith("watch.");
|
||||
}
|
||||
|
||||
QString WatchData::shadowedName(const QString &name, int seen)
|
||||
bool WatchItem::isInspect() const
|
||||
{
|
||||
if (seen <= 0)
|
||||
return name;
|
||||
return shadowedNameFormat().arg(name).arg(seen);
|
||||
if (arrayIndex >= 0)
|
||||
if (const WatchItem *p = parentItem())
|
||||
return p->isInspect();
|
||||
return iname.startsWith("inspect.");
|
||||
}
|
||||
|
||||
QByteArray WatchData::hexAddress() const
|
||||
quint64 WatchItem::realAddress() const
|
||||
{
|
||||
if (address)
|
||||
return QByteArray("0x") + QByteArray::number(address, 16);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
//
|
||||
// Protocol convienience
|
||||
//
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
void WatchData::updateValue(const GdbMi &item)
|
||||
{
|
||||
GdbMi value = item["value"];
|
||||
GdbMi encoding = item["valueencoded"];
|
||||
if (value.isValid() || encoding.isValid()) {
|
||||
setValue(decodeData(value.data(), encoding.data()));
|
||||
} else {
|
||||
setValueNeeded();
|
||||
if (arrayIndex >= 0) {
|
||||
if (const WatchItem *p = parentItem())
|
||||
return p->address + arrayIndex * size;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
void WatchData::updateChildCount(const GdbMi &mi)
|
||||
QByteArray WatchItem::internalName() const
|
||||
{
|
||||
if (mi.isValid())
|
||||
setHasChildren(mi.toInt() > 0);
|
||||
}
|
||||
|
||||
static void setWatchDataValueEnabled(WatchData &data, const GdbMi &mi)
|
||||
{
|
||||
if (mi.data() == "true")
|
||||
data.valueEnabled = true;
|
||||
else if (mi.data() == "false")
|
||||
data.valueEnabled = false;
|
||||
}
|
||||
|
||||
static void setWatchDataValueEditable(WatchData &data, const GdbMi &mi)
|
||||
{
|
||||
if (mi.data() == "true")
|
||||
data.valueEditable = true;
|
||||
else if (mi.data() == "false")
|
||||
data.valueEditable = false;
|
||||
}
|
||||
|
||||
static void setWatchDataAddress(WatchData &data, quint64 address)
|
||||
{
|
||||
data.address = address;
|
||||
|
||||
if (data.exp.isEmpty()) {
|
||||
if (data.iname.startsWith("local.") && data.iname.count('.') == 1)
|
||||
// Solve one common case of adding 'class' in
|
||||
// *(class X*)0xdeadbeef for gdb.
|
||||
data.exp = data.name.toLatin1();
|
||||
else
|
||||
data.exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.hexAddress();
|
||||
if (arrayIndex >= 0) {
|
||||
if (const WatchItem *p = parentItem())
|
||||
return p->iname + '.' + QByteArray::number(arrayIndex);
|
||||
}
|
||||
return iname;
|
||||
}
|
||||
|
||||
static void setWatchDataSize(WatchData &data, const GdbMi &mi)
|
||||
QString WatchItem::realName() const
|
||||
{
|
||||
if (mi.isValid()) {
|
||||
bool ok = false;
|
||||
const unsigned size = mi.data().toUInt(&ok);
|
||||
if (ok)
|
||||
data.size = size;
|
||||
if (arrayIndex >= 0)
|
||||
return QString::fromLatin1("[%1]").arg(arrayIndex);
|
||||
return name;
|
||||
}
|
||||
|
||||
QString WatchItem::expression() const
|
||||
{
|
||||
if (!exp.isEmpty())
|
||||
return QString::fromLatin1(exp);
|
||||
if (quint64 addr = realAddress()) {
|
||||
if (!type.isEmpty())
|
||||
return QString::fromLatin1("*(%1*)0x%2").arg(QLatin1String(type)).arg(addr, 0, 16);
|
||||
}
|
||||
const WatchItem *p = parentItem();
|
||||
if (p && !p->exp.isEmpty())
|
||||
return QString::fromLatin1("(%1).%2").arg(QString::fromLatin1(p->exp), name);
|
||||
return name;
|
||||
}
|
||||
|
||||
void WatchData::updateType(const GdbMi &item)
|
||||
WatchItem *WatchItem::findItem(const QByteArray &iname)
|
||||
{
|
||||
if (item.isValid())
|
||||
setType(item.data());
|
||||
}
|
||||
|
||||
// Utilities to decode string data returned by the dumper helpers.
|
||||
|
||||
template <class T>
|
||||
QString decodeItemHelper(const T &t)
|
||||
{
|
||||
return QString::number(t);
|
||||
}
|
||||
|
||||
QString decodeItemHelper(const double &t)
|
||||
{
|
||||
return QString::number(t, 'g', 16);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void decodeArrayHelper(std::function<void(const WatchData &)> itemHandler, const WatchData &tmplate,
|
||||
const QByteArray &rawData)
|
||||
{
|
||||
const QByteArray ba = QByteArray::fromHex(rawData);
|
||||
const T *p = (const T *) ba.data();
|
||||
WatchData data;
|
||||
const QByteArray exp = "*(" + gdbQuoteTypes(tmplate.type) + "*)0x";
|
||||
for (int i = 0, n = ba.size() / sizeof(T); i < n; ++i) {
|
||||
data = tmplate;
|
||||
data.iname += QByteArray::number(i);
|
||||
data.name = QString::fromLatin1("[%1]").arg(i);
|
||||
data.value = decodeItemHelper(p[i]);
|
||||
data.address += i * sizeof(T);
|
||||
data.exp = exp + QByteArray::number(data.address, 16);
|
||||
data.setAllUnneeded();
|
||||
itemHandler(data);
|
||||
if (internalName() == iname)
|
||||
return this;
|
||||
foreach (TreeItem *child, children()) {
|
||||
auto witem = static_cast<WatchItem *>(child);
|
||||
if (WatchItem *result = witem->findItem(iname))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void decodeArrayData(std::function<void(const WatchData &)> itemHandler, const WatchData &tmplate,
|
||||
const QByteArray &rawData, const DebuggerEncoding &encoding)
|
||||
{
|
||||
switch (encoding.type) {
|
||||
case DebuggerEncoding::HexEncodedSignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
return decodeArrayHelper<signed char>(itemHandler, tmplate, rawData);
|
||||
case 2:
|
||||
return decodeArrayHelper<short>(itemHandler, tmplate, rawData);
|
||||
case 4:
|
||||
return decodeArrayHelper<int>(itemHandler, tmplate, rawData);
|
||||
case 8:
|
||||
return decodeArrayHelper<qint64>(itemHandler, tmplate, rawData);
|
||||
}
|
||||
break;
|
||||
case DebuggerEncoding::HexEncodedUnsignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
return decodeArrayHelper<uchar>(itemHandler, tmplate, rawData);
|
||||
case 2:
|
||||
return decodeArrayHelper<ushort>(itemHandler, tmplate, rawData);
|
||||
case 4:
|
||||
return decodeArrayHelper<uint>(itemHandler, tmplate, rawData);
|
||||
case 8:
|
||||
return decodeArrayHelper<quint64>(itemHandler, tmplate, rawData);
|
||||
}
|
||||
break;
|
||||
case DebuggerEncoding::HexEncodedFloat:
|
||||
switch (encoding.size) {
|
||||
case 4:
|
||||
return decodeArrayHelper<float>(itemHandler, tmplate, rawData);
|
||||
case 8:
|
||||
return decodeArrayHelper<double>(itemHandler, tmplate, rawData);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qDebug() << "ENCODING ERROR: " << encoding.toString();
|
||||
}
|
||||
|
||||
void parseChildrenData(const WatchData &data0, const GdbMi &item,
|
||||
std::function<void(const WatchData &)> itemHandler,
|
||||
std::function<void(const WatchData &, const GdbMi &)> childHandler,
|
||||
std::function<void(const WatchData &childTemplate, const QByteArray &encodedData,
|
||||
const DebuggerEncoding &encoding)> arrayDecoder)
|
||||
{
|
||||
WatchData data = data0;
|
||||
data.setChildrenUnneeded();
|
||||
|
||||
GdbMi children = item["children"];
|
||||
|
||||
data.updateType(item["type"]);
|
||||
|
||||
data.editvalue = item["editvalue"].data();
|
||||
data.editformat = DebuggerDisplay(item["editformat"].toInt());
|
||||
data.editencoding = DebuggerEncoding(item["editencoding"].data());
|
||||
|
||||
GdbMi mi = item["valueelided"];
|
||||
if (mi.isValid())
|
||||
data.elided = mi.toInt();
|
||||
|
||||
mi = item["bitpos"];
|
||||
if (mi.isValid())
|
||||
data.bitpos = mi.toInt();
|
||||
|
||||
mi = item["bitsize"];
|
||||
if (mi.isValid())
|
||||
data.bitsize = mi.toInt();
|
||||
|
||||
mi = item["origaddr"];
|
||||
if (mi.isValid())
|
||||
data.origaddr = mi.toAddress();
|
||||
|
||||
mi = item["address"];
|
||||
if (mi.isValid())
|
||||
setWatchDataAddress(data, mi.toAddress());
|
||||
|
||||
data.updateValue(item);
|
||||
|
||||
setWatchDataSize(data, item["size"]);
|
||||
|
||||
mi = item["exp"];
|
||||
if (mi.isValid())
|
||||
data.exp = mi.data();
|
||||
|
||||
setWatchDataValueEnabled(data, item["valueenabled"]);
|
||||
setWatchDataValueEditable(data, item["valueeditable"]);
|
||||
data.updateChildCount(item["numchild"]); // GDB/MI
|
||||
data.updateChildCount(item["haschild"]); // native-mixed
|
||||
itemHandler(data);
|
||||
|
||||
bool ok = false;
|
||||
qulonglong addressBase = item["addrbase"].data().toULongLong(&ok, 0);
|
||||
qulonglong addressStep = item["addrstep"].data().toULongLong(&ok, 0);
|
||||
|
||||
// Try not to repeat data too often.
|
||||
WatchData childtemplate;
|
||||
childtemplate.updateType(item["childtype"]);
|
||||
childtemplate.updateChildCount(item["childnumchild"]);
|
||||
|
||||
mi = item["arraydata"];
|
||||
if (mi.isValid()) {
|
||||
DebuggerEncoding encoding(item["arrayencoding"].data());
|
||||
childtemplate.iname = data.iname + '.';
|
||||
childtemplate.address = addressBase;
|
||||
arrayDecoder(childtemplate, mi.data(), encoding);
|
||||
} else {
|
||||
for (int i = 0, n = int(children.children().size()); i != n; ++i) {
|
||||
const GdbMi &child = children.children().at(i);
|
||||
WatchData data1 = childtemplate;
|
||||
GdbMi name = child["name"];
|
||||
if (name.isValid())
|
||||
data1.name = QString::fromLatin1(name.data());
|
||||
else
|
||||
data1.name = QString::number(i);
|
||||
GdbMi iname = child["iname"];
|
||||
if (iname.isValid()) {
|
||||
data1.iname = iname.data();
|
||||
} else {
|
||||
data1.iname = data.iname;
|
||||
data1.iname += '.';
|
||||
data1.iname += data1.name.toLatin1();
|
||||
}
|
||||
if (!data1.name.isEmpty() && data1.name.at(0).isDigit())
|
||||
data1.name = QLatin1Char('[') + data1.name + QLatin1Char(']');
|
||||
if (addressStep) {
|
||||
setWatchDataAddress(data1, addressBase);
|
||||
addressBase += addressStep;
|
||||
}
|
||||
QByteArray key = child["key"].data();
|
||||
if (!key.isEmpty()) {
|
||||
data1.name = decodeData(key, child["keyencoded"].data());
|
||||
}
|
||||
childHandler(data1, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseWatchData(const WatchData &data0, const GdbMi &input,
|
||||
QList<WatchData> *list)
|
||||
{
|
||||
auto itemHandler = [list](const WatchData &data) {
|
||||
list->append(data);
|
||||
};
|
||||
auto childHandler = [list](const WatchData &innerData, const GdbMi &innerInput) {
|
||||
parseWatchData(innerData, innerInput, list);
|
||||
};
|
||||
auto arrayDecoder = [itemHandler](const WatchData &childTemplate,
|
||||
const QByteArray &encodedData, const DebuggerEncoding &encoding) {
|
||||
decodeArrayData(itemHandler, childTemplate, encodedData, encoding);
|
||||
};
|
||||
|
||||
parseChildrenData(data0, input, itemHandler, childHandler, arrayDecoder);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void readNumericVectorHelper(std::vector<double> *v, const QByteArray &ba)
|
||||
{
|
||||
const T *p = (const T *) ba.data();
|
||||
const int n = ba.size() / sizeof(T);
|
||||
v->resize(n);
|
||||
// Losing precision in case of 64 bit ints is ok here, as the result
|
||||
// is only used to plot data.
|
||||
for (int i = 0; i != n; ++i)
|
||||
(*v)[i] = static_cast<double>(p[i]);
|
||||
}
|
||||
|
||||
void readNumericVector(std::vector<double> *v, const QByteArray &rawData, const DebuggerEncoding &encoding)
|
||||
{
|
||||
switch (encoding.type) {
|
||||
case DebuggerEncoding::HexEncodedSignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
readNumericVectorHelper<signed char>(v, rawData);
|
||||
return;
|
||||
case 2:
|
||||
readNumericVectorHelper<short>(v, rawData);
|
||||
return;
|
||||
case 4:
|
||||
readNumericVectorHelper<int>(v, rawData);
|
||||
return;
|
||||
case 8:
|
||||
readNumericVectorHelper<qint64>(v, rawData);
|
||||
return;
|
||||
}
|
||||
case DebuggerEncoding::HexEncodedUnsignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
readNumericVectorHelper<uchar>(v, rawData);
|
||||
return;
|
||||
case 2:
|
||||
readNumericVectorHelper<ushort>(v, rawData);
|
||||
return;
|
||||
case 4:
|
||||
readNumericVectorHelper<uint>(v, rawData);
|
||||
return;
|
||||
case 8:
|
||||
readNumericVectorHelper<quint64>(v, rawData);
|
||||
return;
|
||||
}
|
||||
case DebuggerEncoding::HexEncodedFloat:
|
||||
switch (encoding.size) {
|
||||
case 4:
|
||||
readNumericVectorHelper<float>(v, rawData);
|
||||
return;
|
||||
case 8:
|
||||
readNumericVectorHelper<double>(v, rawData);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qDebug() << "ENCODING ERROR: " << encoding.toString();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -33,10 +33,11 @@
|
||||
|
||||
#include "debuggerprotocol.h"
|
||||
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QMetaType>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace Debugger {
|
||||
@@ -44,20 +45,35 @@ namespace Internal {
|
||||
|
||||
class GdbMi;
|
||||
|
||||
class WatchData
|
||||
class WatchItem : public Utils::TreeItem
|
||||
{
|
||||
public:
|
||||
WatchData();
|
||||
WatchItem();
|
||||
|
||||
void parse(const GdbMi &input);
|
||||
|
||||
bool isLocal() const;
|
||||
bool isWatcher() const;
|
||||
bool isInspect() const;
|
||||
|
||||
QString expression() const;
|
||||
QString realName() const;
|
||||
quint64 realAddress() const;
|
||||
QByteArray internalName() const;
|
||||
QString toToolTip() const;
|
||||
|
||||
QVariant editValue() const;
|
||||
int editType() const;
|
||||
|
||||
WatchItem *findItem(const QByteArray &iname);
|
||||
WatchItem *parentItem() const;
|
||||
|
||||
enum State
|
||||
{
|
||||
HasChildrenNeeded = 1,
|
||||
ValueNeeded = 2,
|
||||
ChildrenNeeded = 8,
|
||||
|
||||
InitialState = ValueNeeded
|
||||
| ChildrenNeeded
|
||||
| HasChildrenNeeded
|
||||
InitialState = ValueNeeded | ChildrenNeeded
|
||||
};
|
||||
|
||||
static const qint64 InvalidId = -1;
|
||||
@@ -73,20 +89,14 @@ public:
|
||||
void setChildrenUnneeded() { state = State(state & ~ChildrenNeeded); }
|
||||
void setHasChildren(bool c) { wantsChildren = c; if (!c) setChildrenUnneeded(); }
|
||||
|
||||
bool isLocal() const { return iname.startsWith("local."); }
|
||||
bool isWatcher() const { return iname.startsWith("watch."); }
|
||||
bool isInspect() const { return iname.startsWith("inspect."); }
|
||||
bool isValid() const { return !iname.isEmpty(); }
|
||||
bool isVTablePointer() const;
|
||||
|
||||
bool isAncestorOf(const QByteArray &childIName) const;
|
||||
|
||||
void setError(const QString &);
|
||||
void setValue(const QString &);
|
||||
void setType(const QByteArray &, bool guessChildrenFromType = true);
|
||||
|
||||
QString toString() const;
|
||||
QString toToolTip() const;
|
||||
|
||||
static QString msgNotInScope();
|
||||
static QString shadowedName(const QString &name, int seen);
|
||||
@@ -94,11 +104,6 @@ public:
|
||||
|
||||
QByteArray hexAddress() const;
|
||||
|
||||
// Protocol interaction.
|
||||
void updateValue(const GdbMi &item);
|
||||
void updateChildCount(const GdbMi &mi);
|
||||
void updateType(const GdbMi &item);
|
||||
|
||||
public:
|
||||
qint64 id; // Token for the engine for internal mapping
|
||||
qint32 state; // 'needed' flags;
|
||||
@@ -116,35 +121,18 @@ public:
|
||||
uint bitpos; // Position within bit fields
|
||||
uint bitsize; // Size in case of bit fields
|
||||
int elided; // Full size if value was cut off, -1 if cut on unknown size, 0 otherwise
|
||||
int arrayIndex; // -1 if not an array member
|
||||
bool wantsChildren;
|
||||
bool valueEnabled; // Value will be enabled or not
|
||||
bool valueEditable; // Value will be editable
|
||||
bool outdated; // \internal item is to be removed.
|
||||
|
||||
private:
|
||||
void parseHelper(const GdbMi &input);
|
||||
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::WatchHandler)
|
||||
};
|
||||
|
||||
void decodeArrayData(std::function<void(const WatchData &)> itemHandler,
|
||||
const WatchData &tmplate,
|
||||
const QByteArray &rawData,
|
||||
const DebuggerEncoding &encoding);
|
||||
|
||||
void readNumericVector(std::vector<double> *,
|
||||
const QByteArray &rawData,
|
||||
const DebuggerEncoding &encoding);
|
||||
|
||||
void parseChildrenData(const WatchData &parent, const GdbMi &child,
|
||||
std::function<void(const WatchData &)> itemHandler,
|
||||
std::function<void(const WatchData &, const GdbMi &)> childHandler,
|
||||
std::function<void(const WatchData &, const QByteArray &, const DebuggerEncoding &)> arrayDecoder);
|
||||
|
||||
void parseWatchData(const WatchData &parent, const GdbMi &child,
|
||||
QList<WatchData> *insertions);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
Q_DECLARE_METATYPE(Debugger::Internal::WatchData)
|
||||
|
||||
|
||||
#endif // DEBUGGER_WATCHDATA_H
|
||||
|
||||
@@ -54,14 +54,14 @@
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QPainter>
|
||||
#include <QProcess>
|
||||
#include <QTabWidget>
|
||||
#include <QTextEdit>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <ctype.h>
|
||||
@@ -85,6 +85,71 @@ static int theUnprintableBase = -1;
|
||||
const char INameProperty[] = "INameProperty";
|
||||
const char KeyProperty[] = "KeyProperty";
|
||||
|
||||
static const WatchModel *watchModel(const WatchItem *item)
|
||||
{
|
||||
return reinterpret_cast<const WatchModel *>(item->model());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void readNumericVectorHelper(std::vector<double> *v, const QByteArray &ba)
|
||||
{
|
||||
const T *p = (const T *) ba.data();
|
||||
const int n = ba.size() / sizeof(T);
|
||||
v->resize(n);
|
||||
// Losing precision in case of 64 bit ints is ok here, as the result
|
||||
// is only used to plot data.
|
||||
for (int i = 0; i != n; ++i)
|
||||
(*v)[i] = static_cast<double>(p[i]);
|
||||
}
|
||||
|
||||
static void readNumericVector(std::vector<double> *v, const QByteArray &rawData, DebuggerEncoding encoding)
|
||||
{
|
||||
switch (encoding.type) {
|
||||
case DebuggerEncoding::HexEncodedSignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
readNumericVectorHelper<signed char>(v, rawData);
|
||||
return;
|
||||
case 2:
|
||||
readNumericVectorHelper<short>(v, rawData);
|
||||
return;
|
||||
case 4:
|
||||
readNumericVectorHelper<int>(v, rawData);
|
||||
return;
|
||||
case 8:
|
||||
readNumericVectorHelper<qint64>(v, rawData);
|
||||
return;
|
||||
}
|
||||
case DebuggerEncoding::HexEncodedUnsignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
readNumericVectorHelper<char>(v, rawData);
|
||||
return;
|
||||
case 2:
|
||||
readNumericVectorHelper<unsigned short>(v, rawData);
|
||||
return;
|
||||
case 4:
|
||||
readNumericVectorHelper<unsigned int>(v, rawData);
|
||||
return;
|
||||
case 8:
|
||||
readNumericVectorHelper<quint64>(v, rawData);
|
||||
return;
|
||||
}
|
||||
case DebuggerEncoding::HexEncodedFloat:
|
||||
switch (encoding.size) {
|
||||
case 4:
|
||||
readNumericVectorHelper<float>(v, rawData);
|
||||
return;
|
||||
case 8:
|
||||
readNumericVectorHelper<double>(v, rawData);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qDebug() << "ENCODING ERROR: " << encoding.toString();
|
||||
}
|
||||
|
||||
static QByteArray stripForFormat(const QByteArray &ba)
|
||||
{
|
||||
QByteArray res;
|
||||
@@ -271,13 +336,20 @@ public:
|
||||
|
||||
static QString nameForFormat(int format);
|
||||
|
||||
QVariant data(const QModelIndex &idx, int role) const;
|
||||
bool setData(const QModelIndex &idx, const QVariant &value, int role);
|
||||
QVariant data(const QModelIndex &idx, int role) const override;
|
||||
bool setData(const QModelIndex &idx, const QVariant &value, int role) override;
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex &idx) const override;
|
||||
bool hasChildren(const QModelIndex &idx) const override;
|
||||
bool canFetchMore(const QModelIndex &idx) const override;
|
||||
void fetchMore(const QModelIndex &idx) override;
|
||||
|
||||
QString displayForAutoTest(const QByteArray &iname) const;
|
||||
void reinitialize(bool includeInspectData = false);
|
||||
|
||||
WatchItem *findItem(const QByteArray &iname) const;
|
||||
const WatchItem *watchItem(const QModelIndex &idx) const;
|
||||
|
||||
void insertItem(WatchItem *item);
|
||||
void reexpandItems();
|
||||
|
||||
@@ -321,11 +393,26 @@ WatchModel::WatchModel(WatchHandler *handler, DebuggerEngine *engine)
|
||||
|
||||
setHeader(QStringList() << tr("Name") << tr("Value") << tr("Type"));
|
||||
auto root = new WatchItem;
|
||||
root->appendChild(m_localsRoot = new WatchItem("local", tr("Locals")));
|
||||
root->appendChild(m_inspectorRoot = new WatchItem("inspect", tr("Inspector")));
|
||||
root->appendChild(m_watchRoot = new WatchItem("watch", tr("Expressions")));
|
||||
root->appendChild(m_returnRoot = new WatchItem("return", tr("Return Value")));
|
||||
root->appendChild(m_tooltipRoot = new WatchItem("tooltip", tr("Tooltip")));
|
||||
m_localsRoot = new WatchItem;
|
||||
m_localsRoot->iname = "local";
|
||||
m_localsRoot->name = tr("Locals");
|
||||
m_inspectorRoot = new WatchItem;
|
||||
m_inspectorRoot->iname = "inspect";
|
||||
m_inspectorRoot->name = tr("Inspector");
|
||||
m_watchRoot = new WatchItem;
|
||||
m_watchRoot->iname = "watch";
|
||||
m_watchRoot->name = tr("Expressions");
|
||||
m_returnRoot = new WatchItem;
|
||||
m_returnRoot->iname = "return";
|
||||
m_returnRoot->name = tr("Return Value");
|
||||
m_tooltipRoot = new WatchItem;
|
||||
m_tooltipRoot->iname = "tooltip";
|
||||
m_tooltipRoot->name = tr("Tooltip");
|
||||
root->appendChild(m_localsRoot);
|
||||
root->appendChild(m_inspectorRoot);
|
||||
root->appendChild(m_watchRoot);
|
||||
root->appendChild(m_returnRoot);
|
||||
root->appendChild(m_tooltipRoot);
|
||||
setRootItem(root);
|
||||
|
||||
m_requestUpdateTimer.setSingleShot(true);
|
||||
@@ -357,18 +444,9 @@ WatchItem *WatchModel::findItem(const QByteArray &iname) const
|
||||
return root()->findItem(iname);
|
||||
}
|
||||
|
||||
WatchItem *WatchItem::findItem(const QByteArray &iname)
|
||||
const WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
|
||||
{
|
||||
if (this->iname == iname)
|
||||
return this;
|
||||
foreach (TreeItem *child, children()) {
|
||||
auto witem = static_cast<WatchItem *>(child);
|
||||
if (witem->iname == iname)
|
||||
return witem;
|
||||
if (witem->isAncestorOf(iname))
|
||||
return witem->findItem(iname);
|
||||
}
|
||||
return 0;
|
||||
return static_cast<WatchItem *>(itemForIndex(idx));
|
||||
}
|
||||
|
||||
static QByteArray parentName(const QByteArray &iname)
|
||||
@@ -516,66 +594,74 @@ static QString quoteUnprintable(const QString &str)
|
||||
return encoded;
|
||||
}
|
||||
|
||||
QString WatchItem::formattedValue() const
|
||||
static int itemFormat(const WatchItem *item)
|
||||
{
|
||||
if (type == "bool") {
|
||||
if (value == QLatin1String("0"))
|
||||
const int individualFormat = theIndividualFormats.value(item->iname, AutomaticFormat);
|
||||
if (individualFormat != AutomaticFormat)
|
||||
return individualFormat;
|
||||
return theTypeFormats.value(stripForFormat(item->type), AutomaticFormat);
|
||||
}
|
||||
|
||||
static QString formattedValue(const WatchItem *item)
|
||||
{
|
||||
if (item->type == "bool") {
|
||||
if (item->value == QLatin1String("0"))
|
||||
return QLatin1String("false");
|
||||
if (value == QLatin1String("1"))
|
||||
if (item->value == QLatin1String("1"))
|
||||
return QLatin1String("true");
|
||||
return value;
|
||||
return item->value;
|
||||
}
|
||||
|
||||
const int format = itemFormat();
|
||||
const int format = itemFormat(item);
|
||||
|
||||
// Append quoted, printable character also for decimal.
|
||||
// FIXME: This is unreliable.
|
||||
if (type.endsWith("char") || type.endsWith("QChar")) {
|
||||
if (item->type.endsWith("char") || item->type.endsWith("QChar")) {
|
||||
bool ok;
|
||||
const int code = value.toInt(&ok);
|
||||
bool isUnsigned = type == "unsigned char" || type == "uchar";
|
||||
return ok ? reformatCharacter(code, format, !isUnsigned) : value;
|
||||
const int code = item->value.toInt(&ok);
|
||||
bool isUnsigned = item->type == "unsigned char" || item->type == "uchar";
|
||||
return ok ? reformatCharacter(code, format, !isUnsigned) : item->value;
|
||||
}
|
||||
|
||||
if (format == HexadecimalIntegerFormat
|
||||
|| format == DecimalIntegerFormat
|
||||
|| format == OctalIntegerFormat
|
||||
|| format == BinaryIntegerFormat) {
|
||||
bool isSigned = value.startsWith(QLatin1Char('-'));
|
||||
quint64 raw = isSigned ? quint64(value.toLongLong()) : value.toULongLong();
|
||||
return reformatInteger(raw, format, size, isSigned);
|
||||
bool isSigned = item->value.startsWith(QLatin1Char('-'));
|
||||
quint64 raw = isSigned ? quint64(item->value.toLongLong()) : item->value.toULongLong();
|
||||
return reformatInteger(raw, format, item->size, isSigned);
|
||||
}
|
||||
|
||||
if (format == ScientificFloatFormat) {
|
||||
double dd = value.toDouble();
|
||||
double dd = item->value.toDouble();
|
||||
return QString::number(dd, 'e');
|
||||
}
|
||||
|
||||
if (format == CompactFloatFormat) {
|
||||
double dd = value.toDouble();
|
||||
double dd = item->value.toDouble();
|
||||
return QString::number(dd, 'g');
|
||||
}
|
||||
|
||||
if (type == "va_list")
|
||||
return value;
|
||||
if (item->type == "va_list")
|
||||
return item->value;
|
||||
|
||||
if (!isPointerType(type) && !isVTablePointer()) {
|
||||
if (!isPointerType(item->type) && !item->isVTablePointer()) {
|
||||
bool ok = false;
|
||||
qulonglong integer = value.toULongLong(&ok, 0);
|
||||
qulonglong integer = item->value.toULongLong(&ok, 0);
|
||||
if (ok) {
|
||||
const int format = itemFormat();
|
||||
return reformatInteger(integer, format, size, false);
|
||||
const int format = itemFormat(item);
|
||||
return reformatInteger(integer, format, item->size, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (elided) {
|
||||
QString v = value;
|
||||
if (item->elided) {
|
||||
QString v = item->value;
|
||||
v.chop(1);
|
||||
QString len = elided > 0 ? QString::number(elided) : QLatin1String("unknown length");
|
||||
QString len = item->elided > 0 ? QString::number(item->elided) : QLatin1String("unknown length");
|
||||
return quoteUnprintable(v) + QLatin1String("\"... (") + len + QLatin1Char(')');
|
||||
}
|
||||
|
||||
return quoteUnprintable(value);
|
||||
return quoteUnprintable(item->value);
|
||||
}
|
||||
|
||||
// Get a pointer address from pointer values reported by the debugger.
|
||||
@@ -636,28 +722,6 @@ QVariant WatchItem::editValue() const
|
||||
return QVariant(quoteUnprintable(stringValue));
|
||||
}
|
||||
|
||||
bool WatchItem::canFetchMore() const
|
||||
{
|
||||
if (!wantsChildren)
|
||||
return false;
|
||||
const WatchModel *model = watchModel();
|
||||
if (!model)
|
||||
return false;
|
||||
if (!model->m_contentsValid && !isInspect())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WatchItem::fetchMore()
|
||||
{
|
||||
WatchModel *model = watchModel();
|
||||
model->m_expandedINames.insert(iname);
|
||||
if (children().isEmpty()) {
|
||||
setChildrenNeeded();
|
||||
model->m_engine->expandItem(iname);
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate value for item view, maintaining quotes.
|
||||
static QString truncateValue(QString v)
|
||||
{
|
||||
@@ -670,42 +734,25 @@ static QString truncateValue(QString v)
|
||||
return v;
|
||||
}
|
||||
|
||||
int WatchItem::itemFormat() const
|
||||
{
|
||||
const int individualFormat = theIndividualFormats.value(iname, AutomaticFormat);
|
||||
if (individualFormat != AutomaticFormat)
|
||||
return individualFormat;
|
||||
return theTypeFormats.value(stripForFormat(type), AutomaticFormat);
|
||||
}
|
||||
|
||||
QString WatchItem::expression() const
|
||||
{
|
||||
if (!exp.isEmpty())
|
||||
return QString::fromLatin1(exp);
|
||||
if (address && !type.isEmpty()) {
|
||||
return QString::fromLatin1("*(%1*)%2").
|
||||
arg(QLatin1String(type), QLatin1String(hexAddress()));
|
||||
}
|
||||
if (const WatchItem *p = parentItem()) {
|
||||
if (!p->exp.isEmpty())
|
||||
return QString::fromLatin1("(%1).%2").arg(QString::fromLatin1(p->exp), name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
QString WatchItem::displayName() const
|
||||
static QString displayName(const WatchItem *item)
|
||||
{
|
||||
QString result;
|
||||
if (!parentItem())
|
||||
return result;
|
||||
if (iname.startsWith("return") && name.startsWith(QLatin1Char('$')))
|
||||
result = WatchModel::tr("returned value");
|
||||
else if (name == QLatin1String("*"))
|
||||
result = QLatin1Char('*') + parentItem()->name;
|
||||
else
|
||||
result = watchModel()->removeNamespaces(name);
|
||||
|
||||
// Simplyfy names that refer to base classes.
|
||||
const WatchItem *p = item->parentItem();
|
||||
if (!p)
|
||||
return result;
|
||||
if (item->arrayIndex >= 0) {
|
||||
result = QString::fromLatin1("[%1]").arg(item->arrayIndex);
|
||||
return result;
|
||||
}
|
||||
if (item->iname.startsWith("return") && item->name.startsWith(QLatin1Char('$')))
|
||||
result = WatchModel::tr("returned value");
|
||||
else if (item->name == QLatin1String("*"))
|
||||
result = QLatin1Char('*') + p->name;
|
||||
else
|
||||
result = watchModel(item)->removeNamespaces(item->name);
|
||||
|
||||
// Simplify names that refer to base classes.
|
||||
if (result.startsWith(QLatin1Char('['))) {
|
||||
result = simplifyType(result);
|
||||
if (result.size() > 30)
|
||||
@@ -715,146 +762,115 @@ QString WatchItem::displayName() const
|
||||
return result;
|
||||
}
|
||||
|
||||
QString WatchItem::displayValue() const
|
||||
static QString displayValue(const WatchItem *item)
|
||||
{
|
||||
QString result = watchModel()->removeNamespaces(truncateValue(formattedValue()));
|
||||
if (result.isEmpty() && address)
|
||||
result += QString::fromLatin1("@0x" + QByteArray::number(address, 16));
|
||||
QString result = watchModel(item)->removeNamespaces(truncateValue(formattedValue(item)));
|
||||
if (result.isEmpty() && item->address)
|
||||
result += QString::fromLatin1("@0x" + QByteArray::number(item->address, 16));
|
||||
// if (origaddr)
|
||||
// result += QString::fromLatin1(" (0x" + QByteArray::number(origaddr, 16) + ')');
|
||||
return result;
|
||||
}
|
||||
|
||||
QString WatchItem::displayType() const
|
||||
static QString displayType(const WatchItem *item)
|
||||
{
|
||||
QString result = niceTypeHelper(type);
|
||||
if (bitsize)
|
||||
result += QString::fromLatin1(":%1").arg(bitsize);
|
||||
QString result = niceTypeHelper(item->type);
|
||||
if (item->bitsize)
|
||||
result += QString::fromLatin1(":%1").arg(item->bitsize);
|
||||
result.remove(QLatin1Char('\''));
|
||||
result = watchModel()->removeNamespaces(result);
|
||||
result = watchModel(item)->removeNamespaces(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
QColor WatchItem::valueColor(int column) const
|
||||
static QColor valueColor(const WatchItem *item, int column)
|
||||
{
|
||||
Theme::Color color = Theme::Debugger_WatchItem_ValueNormal;
|
||||
if (const WatchModel *model = watchModel()) {
|
||||
if (!model->m_contentsValid && !isInspect()) {
|
||||
if (const WatchModel *model = watchModel(item)) {
|
||||
if (!model->m_contentsValid && !item->isInspect()) {
|
||||
color = Theme::Debugger_WatchItem_ValueInvalid;
|
||||
} else if (column == 1) {
|
||||
if (!valueEnabled)
|
||||
if (!item->valueEnabled)
|
||||
color = Theme::Debugger_WatchItem_ValueInvalid;
|
||||
else if (!model->m_contentsValid && !isInspect())
|
||||
else if (!model->m_contentsValid && !item->isInspect())
|
||||
color = Theme::Debugger_WatchItem_ValueInvalid;
|
||||
else if (column == 1 && value.isEmpty()) // This might still show 0x...
|
||||
else if (column == 1 && item->value.isEmpty()) // This might still show 0x...
|
||||
color = Theme::Debugger_WatchItem_ValueInvalid;
|
||||
else if (column == 1 && value != model->m_valueCache.value(iname))
|
||||
else if (column == 1 && item->value != model->m_valueCache.value(item->iname))
|
||||
color = Theme::Debugger_WatchItem_ValueChanged;
|
||||
}
|
||||
}
|
||||
return creatorTheme()->color(color);
|
||||
}
|
||||
|
||||
QVariant WatchItem::data(int column, int role) const
|
||||
static DisplayFormats typeFormatList(const WatchItem *item)
|
||||
{
|
||||
switch (role) {
|
||||
case LocalsEditTypeRole:
|
||||
return QVariant(editType());
|
||||
DisplayFormats formats;
|
||||
|
||||
case LocalsNameRole:
|
||||
return QVariant(name);
|
||||
// Types supported by dumpers:
|
||||
// Hack: Compensate for namespaces.
|
||||
QString t = QLatin1String(stripForFormat(item->type));
|
||||
int pos = t.indexOf(QLatin1String("::Q"));
|
||||
if (pos >= 0 && t.count(QLatin1Char(':')) == 2)
|
||||
t.remove(0, pos + 2);
|
||||
pos = t.indexOf(QLatin1Char('<'));
|
||||
if (pos >= 0)
|
||||
t.truncate(pos);
|
||||
t.replace(QLatin1Char(':'), QLatin1Char('_'));
|
||||
formats << watchModel(item)->m_reportedTypeFormats.value(t);
|
||||
|
||||
case LocalsIntegerBaseRole:
|
||||
if (isPointerType(type)) // Pointers using 0x-convention
|
||||
return QVariant(16);
|
||||
return QVariant(formatToIntegerBase(itemFormat()));
|
||||
if (t.contains(QLatin1Char(']')))
|
||||
formats.append(ArrayPlotFormat);
|
||||
|
||||
case Qt::EditRole: {
|
||||
switch (column) {
|
||||
case 0:
|
||||
return expression();
|
||||
case 1:
|
||||
return editValue();
|
||||
case 2:
|
||||
return QString::fromUtf8(type);
|
||||
}
|
||||
}
|
||||
|
||||
case Qt::DisplayRole: {
|
||||
switch (column) {
|
||||
case 0:
|
||||
return displayName();
|
||||
case 1:
|
||||
return displayValue();
|
||||
case 2:
|
||||
return displayType();
|
||||
}
|
||||
}
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
return boolSetting(UseToolTipsInLocalsView)
|
||||
? toToolTip() : QVariant();
|
||||
|
||||
case Qt::ForegroundRole:
|
||||
return valueColor(column);
|
||||
|
||||
case LocalsExpressionRole:
|
||||
return expression();
|
||||
|
||||
case LocalsRawExpressionRole:
|
||||
return exp;
|
||||
|
||||
case LocalsINameRole:
|
||||
return iname;
|
||||
|
||||
case LocalsExpandedRole:
|
||||
return watchModel()->m_expandedINames.contains(iname);
|
||||
|
||||
case LocalsTypeFormatListRole:
|
||||
return QVariant::fromValue(typeFormatList());
|
||||
|
||||
case LocalsTypeRole:
|
||||
return watchModel()->removeNamespaces(displayType());
|
||||
|
||||
case LocalsRawTypeRole:
|
||||
return QString::fromLatin1(type);
|
||||
|
||||
case LocalsTypeFormatRole:
|
||||
return theTypeFormats.value(stripForFormat(type), AutomaticFormat);
|
||||
|
||||
case LocalsIndividualFormatRole:
|
||||
return theIndividualFormats.value(iname, AutomaticFormat);
|
||||
|
||||
case LocalsRawValueRole:
|
||||
return value;
|
||||
|
||||
case LocalsObjectAddressRole:
|
||||
return address;
|
||||
|
||||
case LocalsPointerAddressRole:
|
||||
return origaddr;
|
||||
|
||||
case LocalsIsWatchpointAtObjectAddressRole: {
|
||||
BreakpointParameters bp(WatchpointAtAddress);
|
||||
bp.address = address;
|
||||
return watchModel()->m_engine->breakHandler()->findWatchpoint(bp) != 0;
|
||||
}
|
||||
|
||||
case LocalsSizeRole:
|
||||
return QVariant(size);
|
||||
|
||||
case LocalsIsWatchpointAtPointerAddressRole:
|
||||
if (isPointerType(type)) {
|
||||
BreakpointParameters bp(WatchpointAtAddress);
|
||||
bp.address = pointerValue(value);
|
||||
return watchModel()->m_engine->breakHandler()->findWatchpoint(bp) != 0;
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
break;
|
||||
// Fixed artificial string and pointer types.
|
||||
if (item->origaddr || isPointerType(item->type)) {
|
||||
formats.append(RawFormat);
|
||||
formats.append(Latin1StringFormat);
|
||||
formats.append(SeparateLatin1StringFormat);
|
||||
formats.append(Utf8StringFormat);
|
||||
formats.append(SeparateUtf8StringFormat);
|
||||
formats.append(Local8BitStringFormat);
|
||||
formats.append(Utf16StringFormat);
|
||||
formats.append(Ucs4StringFormat);
|
||||
formats.append(Array10Format);
|
||||
formats.append(Array100Format);
|
||||
formats.append(Array1000Format);
|
||||
formats.append(Array10000Format);
|
||||
} else if (item->type.contains("char[") || item->type.contains("char [")) {
|
||||
formats.append(RawFormat);
|
||||
formats.append(Latin1StringFormat);
|
||||
formats.append(SeparateLatin1StringFormat);
|
||||
formats.append(Utf8StringFormat);
|
||||
formats.append(SeparateUtf8StringFormat);
|
||||
formats.append(Local8BitStringFormat);
|
||||
formats.append(Utf16StringFormat);
|
||||
formats.append(Ucs4StringFormat);
|
||||
}
|
||||
return QVariant();
|
||||
|
||||
// Fixed artificial floating point types.
|
||||
bool ok = false;
|
||||
item->value.toDouble(&ok);
|
||||
if (ok) {
|
||||
formats.append(CompactFloatFormat);
|
||||
formats.append(ScientificFloatFormat);
|
||||
}
|
||||
|
||||
// Fixed artificial integral types.
|
||||
QString v = item->value;
|
||||
if (v.startsWith(QLatin1Char('-')))
|
||||
v = v.mid(1);
|
||||
v.toULongLong(&ok, 10);
|
||||
if (!ok)
|
||||
v.toULongLong(&ok, 16);
|
||||
if (!ok)
|
||||
v.toULongLong(&ok, 8);
|
||||
if (ok) {
|
||||
formats.append(DecimalIntegerFormat);
|
||||
formats.append(HexadecimalIntegerFormat);
|
||||
formats.append(BinaryIntegerFormat);
|
||||
formats.append(OctalIntegerFormat);
|
||||
}
|
||||
|
||||
return formats;
|
||||
}
|
||||
|
||||
QVariant WatchModel::data(const QModelIndex &idx, int role) const
|
||||
@@ -867,7 +883,109 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
|
||||
l.append(indexForItem(item));
|
||||
return QVariant::fromValue(l);
|
||||
}
|
||||
return WatchModelBase::data(idx, role);
|
||||
const WatchItem *item = watchItem(idx);
|
||||
if (!item)
|
||||
return QVariant();
|
||||
|
||||
const int column = idx.column();
|
||||
switch (role) {
|
||||
case LocalsEditTypeRole:
|
||||
return item->editType();
|
||||
|
||||
case LocalsNameRole:
|
||||
return item->name;
|
||||
|
||||
case LocalsIntegerBaseRole:
|
||||
if (isPointerType(item->type)) // Pointers using 0x-convention
|
||||
return QVariant(16);
|
||||
return formatToIntegerBase(itemFormat(item));
|
||||
|
||||
case Qt::EditRole: {
|
||||
switch (column) {
|
||||
case 0:
|
||||
return item->expression();
|
||||
case 1:
|
||||
return item->editValue();
|
||||
case 2:
|
||||
return QString::fromUtf8(item->type);
|
||||
}
|
||||
}
|
||||
|
||||
case Qt::DisplayRole: {
|
||||
switch (column) {
|
||||
case 0:
|
||||
return displayName(item);
|
||||
case 1:
|
||||
return displayValue(item);
|
||||
case 2:
|
||||
return displayType(item);
|
||||
}
|
||||
}
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
return boolSetting(UseToolTipsInLocalsView)
|
||||
? item->toToolTip() : QVariant();
|
||||
|
||||
case Qt::ForegroundRole:
|
||||
return valueColor(item, column);
|
||||
|
||||
case LocalsExpressionRole:
|
||||
return item->expression();
|
||||
|
||||
case LocalsRawExpressionRole:
|
||||
return item->exp;
|
||||
|
||||
case LocalsINameRole:
|
||||
return item->iname;
|
||||
|
||||
case LocalsExpandedRole:
|
||||
return m_expandedINames.contains(item->iname);
|
||||
|
||||
case LocalsTypeFormatListRole:
|
||||
return QVariant::fromValue(typeFormatList(item));
|
||||
|
||||
case LocalsTypeRole:
|
||||
return removeNamespaces(displayType(item));
|
||||
|
||||
case LocalsRawTypeRole:
|
||||
return QString::fromLatin1(item->type);
|
||||
|
||||
case LocalsTypeFormatRole:
|
||||
return theTypeFormats.value(stripForFormat(item->type), AutomaticFormat);
|
||||
|
||||
case LocalsIndividualFormatRole:
|
||||
return theIndividualFormats.value(item->iname, AutomaticFormat);
|
||||
|
||||
case LocalsRawValueRole:
|
||||
return item->value;
|
||||
|
||||
case LocalsObjectAddressRole:
|
||||
return item->address;
|
||||
|
||||
case LocalsPointerAddressRole:
|
||||
return item->origaddr;
|
||||
|
||||
case LocalsIsWatchpointAtObjectAddressRole: {
|
||||
BreakpointParameters bp(WatchpointAtAddress);
|
||||
bp.address = item->address;
|
||||
return m_engine->breakHandler()->findWatchpoint(bp) != 0;
|
||||
}
|
||||
|
||||
case LocalsSizeRole:
|
||||
return item->size;
|
||||
|
||||
case LocalsIsWatchpointAtPointerAddressRole:
|
||||
if (isPointerType(item->type)) {
|
||||
BreakpointParameters bp(WatchpointAtAddress);
|
||||
bp.address = pointerValue(item->value);
|
||||
return m_engine->breakHandler()->findWatchpoint(bp) != 0;
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role)
|
||||
@@ -920,12 +1038,19 @@ bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role
|
||||
return true;
|
||||
}
|
||||
|
||||
Qt::ItemFlags WatchItem::flags(int column) const
|
||||
Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
|
||||
{
|
||||
QTC_ASSERT(model(), return Qt::ItemFlags());
|
||||
DebuggerEngine *engine = watchModel()->m_engine;
|
||||
QTC_ASSERT(engine, return Qt::ItemFlags());
|
||||
const DebuggerState state = engine->state();
|
||||
if (!idx.isValid())
|
||||
return 0;
|
||||
|
||||
const WatchItem *item = watchItem(idx);
|
||||
if (!item)
|
||||
return Qt::ItemIsEnabled|Qt::ItemIsSelectable;
|
||||
|
||||
const int column = idx.column();
|
||||
|
||||
QTC_ASSERT(m_engine, return Qt::ItemFlags());
|
||||
const DebuggerState state = m_engine->state();
|
||||
|
||||
// Enabled, editable, selectable, checkable, and can be used both as the
|
||||
// source of a drag and drop operation and as a drop target.
|
||||
@@ -933,37 +1058,88 @@ Qt::ItemFlags WatchItem::flags(int column) const
|
||||
const Qt::ItemFlags notEditable = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||
const Qt::ItemFlags editable = notEditable | Qt::ItemIsEditable;
|
||||
|
||||
if (state == InferiorUnrunnable)
|
||||
return (isWatcher() && column == 0 && iname.count('.') == 1) ? editable : notEditable;
|
||||
if (item->isWatcher()) {
|
||||
if (state == InferiorUnrunnable)
|
||||
return (column == 0 && item->iname.count('.') == 1) ? editable : notEditable;
|
||||
|
||||
if (isWatcher()) {
|
||||
if (state != InferiorStopOk
|
||||
&& state != DebuggerNotReady
|
||||
&& state != DebuggerFinished
|
||||
&& !engine->hasCapability(AddWatcherWhileRunningCapability))
|
||||
&& !m_engine->hasCapability(AddWatcherWhileRunningCapability))
|
||||
return Qt::ItemFlags();
|
||||
if (column == 0 && iname.count('.') == 1)
|
||||
if (column == 0 && item->iname.count('.') == 1)
|
||||
return editable; // Watcher names are editable.
|
||||
if (column == 1 && item->arrayIndex >= 0)
|
||||
return editable;
|
||||
|
||||
if (!name.isEmpty()) {
|
||||
if (!item->name.isEmpty()) {
|
||||
// FIXME: Forcing types is not implemented yet.
|
||||
//if (idx.column() == 2)
|
||||
// return editable; // Watcher types can be set by force.
|
||||
if (column == 1 && valueEditable && !elided)
|
||||
if (column == 1 && item->valueEditable && !item->elided)
|
||||
return editable; // Watcher values are sometimes editable.
|
||||
}
|
||||
} else if (isLocal()) {
|
||||
if (state != InferiorStopOk && !engine->hasCapability(AddWatcherWhileRunningCapability))
|
||||
} else if (item->isLocal()) {
|
||||
if (state != InferiorStopOk && !m_engine->hasCapability(AddWatcherWhileRunningCapability))
|
||||
return Qt::ItemFlags();
|
||||
if (column == 1 && valueEditable && !elided)
|
||||
if (column == 1 && item->valueEditable && !item->elided)
|
||||
return editable; // Locals values are sometimes editable.
|
||||
} else if (isInspect()) {
|
||||
if (column == 1 && valueEditable)
|
||||
if (column == 1 && item->arrayIndex >= 0)
|
||||
return editable;
|
||||
} else if (item->isInspect()) {
|
||||
if (column == 1 && item->valueEditable)
|
||||
return editable; // Inspector values are sometimes editable.
|
||||
}
|
||||
return notEditable;
|
||||
}
|
||||
|
||||
bool WatchModel::canFetchMore(const QModelIndex &idx) const
|
||||
{
|
||||
if (!idx.isValid())
|
||||
return false;
|
||||
|
||||
// See "hasChildren" below.
|
||||
const WatchItem *item = watchItem(idx);
|
||||
if (!item)
|
||||
return false;
|
||||
if (!item->wantsChildren)
|
||||
return false;
|
||||
if (!m_contentsValid && !item->isInspect())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WatchModel::fetchMore(const QModelIndex &idx)
|
||||
{
|
||||
if (!idx.isValid())
|
||||
return;
|
||||
|
||||
WatchItem *item = static_cast<WatchItem *>(itemForIndex(idx));
|
||||
if (item) {
|
||||
m_expandedINames.insert(item->iname);
|
||||
if (item->children().isEmpty()) {
|
||||
item->setChildrenNeeded();
|
||||
m_engine->expandItem(item->iname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WatchModel::hasChildren(const QModelIndex &idx) const
|
||||
{
|
||||
const WatchItem *item = watchItem(idx);
|
||||
if (!item)
|
||||
return true;
|
||||
if (item->rowCount() > 0)
|
||||
return true;
|
||||
|
||||
// "Can fetch more", see above.
|
||||
if (!item->wantsChildren)
|
||||
return false;
|
||||
if (!m_contentsValid && !item->isInspect())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline QString msgArrayFormat(int n)
|
||||
{
|
||||
return WatchModel::tr("Array of %n items", 0, n);
|
||||
@@ -1013,85 +1189,6 @@ QString WatchModel::nameForFormat(int format)
|
||||
return QString();
|
||||
}
|
||||
|
||||
DisplayFormats WatchItem::typeFormatList() const
|
||||
{
|
||||
DisplayFormats formats;
|
||||
|
||||
// Types supported by dumpers:
|
||||
// Hack: Compensate for namespaces.
|
||||
QString t = QLatin1String(stripForFormat(type));
|
||||
int pos = t.indexOf(QLatin1String("::Q"));
|
||||
if (pos >= 0 && t.count(QLatin1Char(':')) == 2)
|
||||
t.remove(0, pos + 2);
|
||||
pos = t.indexOf(QLatin1Char('<'));
|
||||
if (pos >= 0)
|
||||
t.truncate(pos);
|
||||
t.replace(QLatin1Char(':'), QLatin1Char('_'));
|
||||
formats << watchModel()->m_reportedTypeFormats.value(t);
|
||||
|
||||
if (t.contains(QLatin1Char(']')))
|
||||
formats.append(ArrayPlotFormat);
|
||||
|
||||
// Fixed artificial string and pointer types.
|
||||
if (origaddr || isPointerType(type)) {
|
||||
formats.append(RawFormat);
|
||||
formats.append(Latin1StringFormat);
|
||||
formats.append(SeparateLatin1StringFormat);
|
||||
formats.append(Utf8StringFormat);
|
||||
formats.append(SeparateUtf8StringFormat);
|
||||
formats.append(Local8BitStringFormat);
|
||||
formats.append(Utf16StringFormat);
|
||||
formats.append(Ucs4StringFormat);
|
||||
formats.append(Array10Format);
|
||||
formats.append(Array100Format);
|
||||
formats.append(Array1000Format);
|
||||
formats.append(Array10000Format);
|
||||
} else if (type.contains("char[") || type.contains("char [")) {
|
||||
formats.append(RawFormat);
|
||||
formats.append(Latin1StringFormat);
|
||||
formats.append(SeparateLatin1StringFormat);
|
||||
formats.append(Utf8StringFormat);
|
||||
formats.append(SeparateUtf8StringFormat);
|
||||
formats.append(Local8BitStringFormat);
|
||||
formats.append(Utf16StringFormat);
|
||||
formats.append(Ucs4StringFormat);
|
||||
}
|
||||
|
||||
// Fixed artificial floating point types.
|
||||
bool ok = false;
|
||||
value.toDouble(&ok);
|
||||
if (ok) {
|
||||
formats.append(CompactFloatFormat);
|
||||
formats.append(ScientificFloatFormat);
|
||||
}
|
||||
|
||||
// Fixed artificial integral types.
|
||||
QString v = value;
|
||||
if (v.startsWith(QLatin1Char('-')))
|
||||
v = v.mid(1);
|
||||
v.toULongLong(&ok, 10);
|
||||
if (!ok)
|
||||
v.toULongLong(&ok, 16);
|
||||
if (!ok)
|
||||
v.toULongLong(&ok, 8);
|
||||
if (ok) {
|
||||
formats.append(DecimalIntegerFormat);
|
||||
formats.append(HexadecimalIntegerFormat);
|
||||
formats.append(BinaryIntegerFormat);
|
||||
formats.append(OctalIntegerFormat);
|
||||
}
|
||||
|
||||
return formats;
|
||||
}
|
||||
|
||||
int WatchItem::requestedFormat() const
|
||||
{
|
||||
int format = theIndividualFormats.value(iname, AutomaticFormat);
|
||||
if (format == AutomaticFormat)
|
||||
format = theTypeFormats.value(stripForFormat(type), AutomaticFormat);
|
||||
return format;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WatchHandler
|
||||
@@ -1316,7 +1413,7 @@ void WatchHandler::updateWatchExpression(WatchItem *item, const QByteArray &newE
|
||||
// (address) if it can be found. Default to watchExpression().
|
||||
void WatchHandler::watchVariable(const QString &exp)
|
||||
{
|
||||
if (const WatchData *localVariable = findCppLocalVariable(exp))
|
||||
if (const WatchItem *localVariable = findCppLocalVariable(exp))
|
||||
watchExpression(QLatin1String(localVariable->exp), exp);
|
||||
else
|
||||
watchExpression(exp);
|
||||
@@ -1489,8 +1586,13 @@ const WatchItem *WatchHandler::watchItem(const QModelIndex &idx) const
|
||||
|
||||
void WatchHandler::fetchMore(const QByteArray &iname) const
|
||||
{
|
||||
if (WatchItem *item = m_model->findItem(iname))
|
||||
item->fetchMore();
|
||||
if (WatchItem *item = m_model->findItem(iname)) {
|
||||
m_model->m_expandedINames.insert(iname);
|
||||
if (item->children().isEmpty()) {
|
||||
item->setChildrenNeeded();
|
||||
m_model->m_engine->expandItem(iname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WatchItem *WatchHandler::findItem(const QByteArray &iname) const
|
||||
@@ -1723,77 +1825,5 @@ QSet<QByteArray> WatchHandler::expandedINames() const
|
||||
return m_model->m_expandedINames;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WatchItem
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
WatchItem::WatchItem(const QByteArray &i, const QString &n)
|
||||
{
|
||||
iname = i;
|
||||
name = n;
|
||||
}
|
||||
|
||||
WatchItem::WatchItem(const WatchData &data)
|
||||
: WatchData(data)
|
||||
{
|
||||
}
|
||||
|
||||
WatchItem::WatchItem(const GdbMi &data)
|
||||
{
|
||||
iname = data["iname"].data();
|
||||
|
||||
GdbMi wname = data["wname"];
|
||||
if (wname.isValid()) // Happens (only) for watched expressions.
|
||||
name = QString::fromUtf8(QByteArray::fromHex(wname.data()));
|
||||
else
|
||||
name = QString::fromLatin1(data["name"].data());
|
||||
|
||||
parseWatchData(data);
|
||||
|
||||
if (wname.isValid())
|
||||
exp = name.toUtf8();
|
||||
}
|
||||
|
||||
WatchItem *WatchItem::parentItem() const
|
||||
{
|
||||
return dynamic_cast<WatchItem *>(parent());
|
||||
}
|
||||
|
||||
const WatchModel *WatchItem::watchModel() const
|
||||
{
|
||||
return static_cast<const WatchModel *>(model());
|
||||
}
|
||||
|
||||
WatchModel *WatchItem::watchModel()
|
||||
{
|
||||
return static_cast<WatchModel *>(model());
|
||||
}
|
||||
|
||||
void WatchItem::parseWatchData(const GdbMi &input)
|
||||
{
|
||||
auto itemHandler = [this](const WatchData &data) {
|
||||
static_cast<WatchData *>(this)->operator=(data); // FIXME with 3.5
|
||||
};
|
||||
|
||||
auto childHandler = [this](const WatchData &innerData, const GdbMi &innerInput) {
|
||||
WatchItem *item = new WatchItem(innerData);
|
||||
item->parseWatchData(innerInput);
|
||||
appendChild(item);
|
||||
};
|
||||
|
||||
auto itemAdder = [this](const WatchData &data) {
|
||||
appendChild(new WatchItem(data));
|
||||
};
|
||||
|
||||
auto arrayDecoder = [itemAdder](const WatchData &childTemplate,
|
||||
const QByteArray &encodedData, const DebuggerEncoding &encoding) {
|
||||
decodeArrayData(itemAdder, childTemplate, encodedData, encoding);
|
||||
};
|
||||
|
||||
parseChildrenData(*this, input, itemHandler, childHandler, arrayDecoder);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
@@ -33,8 +33,6 @@
|
||||
|
||||
#include "watchdata.h"
|
||||
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
#include <QVector>
|
||||
|
||||
namespace Debugger {
|
||||
@@ -46,44 +44,6 @@ class WatchModel;
|
||||
|
||||
typedef QVector<DisplayFormat> DisplayFormats;
|
||||
|
||||
class WatchItem : public Utils::TreeItem, public WatchData
|
||||
{
|
||||
public:
|
||||
WatchItem() {}
|
||||
WatchItem(const QByteArray &i, const QString &n);
|
||||
explicit WatchItem(const WatchData &data);
|
||||
explicit WatchItem(const GdbMi &data);
|
||||
|
||||
void fetchMore();
|
||||
|
||||
QString displayName() const;
|
||||
QString displayType() const;
|
||||
QString displayValue() const;
|
||||
QString formattedValue() const;
|
||||
QString expression() const;
|
||||
|
||||
int itemFormat() const;
|
||||
|
||||
QVariant editValue() const;
|
||||
int editType() const;
|
||||
QColor valueColor(int column) const;
|
||||
|
||||
int requestedFormat() const;
|
||||
WatchItem *findItem(const QByteArray &iname);
|
||||
|
||||
private:
|
||||
WatchItem *parentItem() const;
|
||||
const WatchModel *watchModel() const;
|
||||
WatchModel *watchModel();
|
||||
DisplayFormats typeFormatList() const;
|
||||
|
||||
bool canFetchMore() const;
|
||||
QVariant data(int column, int role) const;
|
||||
Qt::ItemFlags flags(int column) const;
|
||||
|
||||
void parseWatchData(const GdbMi &input);
|
||||
};
|
||||
|
||||
class WatchModelBase : public Utils::TreeModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
QT = core network
|
||||
|
||||
win32-msvc* {
|
||||
QTC_LIB_DEPENDS += utils
|
||||
}
|
||||
QTC_LIB_DEPENDS += utils
|
||||
|
||||
include(../qttest.pri)
|
||||
|
||||
|
||||
@@ -1425,62 +1425,65 @@ void tst_Dumpers::dumper()
|
||||
GdbMi actual;
|
||||
actual.fromString(contents);
|
||||
|
||||
WatchData local;
|
||||
WatchItem local;
|
||||
local.iname = "local";
|
||||
|
||||
QList<WatchData> list;
|
||||
foreach (const GdbMi &child, actual.children()) {
|
||||
WatchData dummy;
|
||||
dummy.iname = child["iname"].data();
|
||||
dummy.name = QLatin1String(child["name"].data());
|
||||
if (dummy.iname == "local.qtversion")
|
||||
const QByteArray iname = child["iname"].data();
|
||||
if (iname == "local.qtversion")
|
||||
context.qtVersion = child["value"].toInt();
|
||||
else if (dummy.iname == "local.gccversion")
|
||||
else if (iname == "local.gccversion")
|
||||
context.gccVersion = child["value"].toInt();
|
||||
else if (dummy.iname == "local.clangversion")
|
||||
else if (iname == "local.clangversion")
|
||||
context.clangVersion = child["value"].toInt();
|
||||
else if (dummy.iname == "local.boostversion")
|
||||
else if (iname == "local.boostversion")
|
||||
context.boostVersion = child["value"].toInt();
|
||||
else
|
||||
parseWatchData(dummy, child, &list);
|
||||
else {
|
||||
WatchItem *item = new WatchItem;
|
||||
item->parse(child);
|
||||
local.appendChild(item);
|
||||
}
|
||||
}
|
||||
|
||||
//qDebug() << "QT VERSION " << QByteArray::number(context.qtVersion, 16);
|
||||
QSet<QByteArray> seenINames;
|
||||
bool ok = true;
|
||||
foreach (const WatchData &item, list) {
|
||||
seenINames.insert(item.iname);
|
||||
|
||||
for (int i = data.checks.size(); --i >= 0; ) {
|
||||
//qDebug() << "NUM CHECKS" << data.checks.size();
|
||||
for (int i = data.checks.size(); --i >= 0; ) {
|
||||
Check check = data.checks.at(i);
|
||||
Check check = data.checks.at(i);
|
||||
QByteArray iname = "local." + check.iname;
|
||||
if (const WatchItem *item = local.findItem(iname)) {
|
||||
seenINames.insert(iname);
|
||||
//qDebug() << "CHECKS" << i << check.iname;
|
||||
if ("local." + check.iname == item.iname) {
|
||||
data.checks.removeAt(i);
|
||||
if (check.matches(m_debuggerEngine, m_debuggerVersion, context)) {
|
||||
//qDebug() << "USING MATCHING TEST FOR " << item.iname;
|
||||
if (!check.expectedName.matches(item.name.toLatin1(), context)) {
|
||||
qDebug() << "INAME : " << item.iname;
|
||||
qDebug() << "NAME ACTUAL : " << item.name.toLatin1();
|
||||
qDebug() << "NAME EXPECTED: " << check.expectedName.name;
|
||||
ok = false;
|
||||
}
|
||||
if (!check.expectedValue.matches(item.value, context)) {
|
||||
qDebug() << "INAME : " << item.iname;
|
||||
qDebug() << "VALUE ACTUAL : " << item.value << toHex(item.value);
|
||||
qDebug() << "VALUE EXPECTED: "
|
||||
<< check.expectedValue.value << toHex(check.expectedValue.value);
|
||||
ok = false;
|
||||
}
|
||||
if (!check.expectedType.matches(item.type, context)) {
|
||||
qDebug() << "INAME : " << item.iname;
|
||||
qDebug() << "TYPE ACTUAL : " << item.type;
|
||||
qDebug() << "TYPE EXPECTED: " << check.expectedType.type;
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "SKIPPING NON-MATCHING TEST FOR " << item.iname;
|
||||
data.checks.removeAt(i);
|
||||
if (check.matches(m_debuggerEngine, m_debuggerVersion, context)) {
|
||||
//qDebug() << "USING MATCHING TEST FOR " << iname;
|
||||
QByteArray name = item->realName().toLatin1();
|
||||
QByteArray type = item->type;
|
||||
if (!check.expectedName.matches(name, context)) {
|
||||
qDebug() << "INAME : " << iname;
|
||||
qDebug() << "NAME ACTUAL : " << name;
|
||||
qDebug() << "NAME EXPECTED: " << check.expectedName.name;
|
||||
ok = false;
|
||||
}
|
||||
if (!check.expectedValue.matches(item->value, context)) {
|
||||
qDebug() << "INAME : " << iname;
|
||||
qDebug() << "VALUE ACTUAL : " << item->value << toHex(item->value);
|
||||
qDebug() << "VALUE EXPECTED: " << check.expectedValue.value << toHex(check.expectedValue.value);
|
||||
ok = false;
|
||||
}
|
||||
if (!check.expectedType.matches(type, context)) {
|
||||
qDebug() << "INAME : " << iname;
|
||||
qDebug() << "TYPE ACTUAL : " << type;
|
||||
qDebug() << "TYPE EXPECTED: " << check.expectedType.type;
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "SKIPPING NON-MATCHING TEST FOR " << iname;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "NOT SEEN: " << check.iname;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user