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

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