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:
hjk
2015-12-16 17:17:38 +01:00
parent 768b775f52
commit 7de7eb6bca
19 changed files with 878 additions and 912 deletions

View File

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

View File

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

View File

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

View File

@@ -63,7 +63,6 @@ class DebuggerEnginePrivate;
class DebuggerPluginPrivate;
class DisassemblerAgent;
class MemoryAgent;
class WatchData;
class WatchItem;
class BreakHandler;
class LocationMark;

View File

@@ -1036,7 +1036,6 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(DebuggerPlugin *plugin) :
m_dummyEngine(0),
m_globalDebuggerOptions(new GlobalDebuggerOptions)
{
qRegisterMetaType<WatchData>("WatchData");
qRegisterMetaType<ContextData>("ContextData");
qRegisterMetaType<DebuggerRunParameters>("DebuggerRunParameters");

View File

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

View File

@@ -52,7 +52,6 @@
namespace Debugger {
namespace Internal {
class WatchData;
class GdbMi;
/* A debugger engine interfacing the LLDB debugger

View File

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

View File

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

View File

@@ -40,7 +40,6 @@
namespace Debugger {
namespace Internal {
class WatchData;
class WatchItem;
class QmlEnginePrivate;
class QmlInspectorAgent;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,6 @@
QT = core network
win32-msvc* {
QTC_LIB_DEPENDS += utils
}
QTC_LIB_DEPENDS += utils
include(../qttest.pri)

View File

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