forked from qt-creator/qt-creator
Debugger: add <load more> functionality to array dumper
Change-Id: Ib44748fa3218788ca20a99b0a0f4cd85716dde06 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -106,7 +106,7 @@ class Children():
|
||||
self.d.putNumChild(0)
|
||||
if self.d.currentMaxNumChild is not None:
|
||||
if self.d.currentMaxNumChild < self.d.currentNumChild:
|
||||
self.d.put('{name="<incomplete>",value="",type="",numchild="0"},')
|
||||
self.d.put('{name="<load more>",value="",type="",numchild="1"},')
|
||||
self.d.currentChildType = self.savedChildType
|
||||
self.d.currentChildNumChild = self.savedChildNumChild
|
||||
self.d.currentNumChild = self.savedNumChild
|
||||
@@ -214,7 +214,7 @@ class DumperBase():
|
||||
|
||||
def setVariableFetchingOptions(self, args):
|
||||
self.resultVarName = args.get('resultvarname', '')
|
||||
self.expandedINames = set(args.get('expanded', []))
|
||||
self.expandedINames = args.get('expanded', {})
|
||||
self.stringCutOff = int(args.get('stringcutoff', 10000))
|
||||
self.displayStringLimit = int(args.get('displaystringlimit', 100))
|
||||
self.typeformats = args.get('typeformats', {})
|
||||
@@ -297,6 +297,11 @@ class DumperBase():
|
||||
return range(0, self.currentNumChild)
|
||||
return range(min(self.currentMaxNumChild, self.currentNumChild))
|
||||
|
||||
def maxArrayCount(self):
|
||||
if self.currentIName in self.expandedINames:
|
||||
return self.expandedINames[self.currentIName]
|
||||
return 100
|
||||
|
||||
def enterSubItem(self, item):
|
||||
if self.useTimeStamps:
|
||||
item.startTime = time.time()
|
||||
@@ -2232,7 +2237,7 @@ class DumperBase():
|
||||
res = self.currentValue
|
||||
return res # The 'short' display.
|
||||
|
||||
def putArrayData(self, base, n, innerType, childNumChild=None, maxNumChild=10000):
|
||||
def putArrayData(self, base, n, innerType, childNumChild=None):
|
||||
self.checkIntType(base)
|
||||
self.checkIntType(n)
|
||||
addrBase = base
|
||||
@@ -2240,6 +2245,7 @@ class DumperBase():
|
||||
self.putNumChild(n)
|
||||
#DumperBase.warn('ADDRESS: 0x%x INNERSIZE: %s INNERTYPE: %s' % (addrBase, innerSize, innerType))
|
||||
enc = innerType.simpleEncoding()
|
||||
maxNumChild = self.maxArrayCount()
|
||||
if enc:
|
||||
self.put('childtype="%s",' % innerType.name)
|
||||
self.put('addrbase="0x%x",' % addrBase)
|
||||
@@ -2247,7 +2253,7 @@ class DumperBase():
|
||||
self.put('arrayencoding="%s",' % enc)
|
||||
self.put('endian="%s",' % self.packCode)
|
||||
if n > maxNumChild:
|
||||
self.put('childrenelided="%s",' % n) # FIXME: Act on that in frontend
|
||||
self.put('childrenelided="%s",' % n)
|
||||
n = maxNumChild
|
||||
self.put('arraydata="')
|
||||
self.put(self.readMemory(addrBase, n * innerSize))
|
||||
@@ -2281,7 +2287,7 @@ class DumperBase():
|
||||
def putPlotData(self, base, n, innerType, maxNumChild=1000 * 1000):
|
||||
self.putPlotDataHelper(base, n, innerType, maxNumChild=maxNumChild)
|
||||
if self.isExpanded():
|
||||
self.putArrayData(base, n, innerType, maxNumChild=maxNumChild)
|
||||
self.putArrayData(base, n, innerType)
|
||||
|
||||
def putSpecialArgv(self, value):
|
||||
"""
|
||||
|
||||
@@ -2120,7 +2120,7 @@ class SummaryDumper(Dumper, LogMixin):
|
||||
|
||||
# Expand variable if we need synthetic children
|
||||
oldExpanded = self.expandedINames
|
||||
self.expandedINames = [value.name] if expanded else []
|
||||
self.expandedINames = {value.name: 100} if expanded else {}
|
||||
|
||||
savedOutput = self.output
|
||||
self.output = []
|
||||
|
||||
@@ -1445,7 +1445,7 @@ class QtcInternalDumper():
|
||||
self.updateData(args)
|
||||
|
||||
def updateData(self, args):
|
||||
self.expandedINames = __builtins__.set(args.get('expanded', []))
|
||||
self.expandedINames = args.get('expanded', {})
|
||||
self.typeformats = args.get('typeformats', {})
|
||||
self.formats = args.get('formats', {})
|
||||
self.output = ''
|
||||
|
||||
@@ -471,7 +471,7 @@ public:
|
||||
QString m_qtNamespace;
|
||||
|
||||
// Safety net to avoid infinite lookups.
|
||||
QSet<QString> m_lookupRequests; // FIXME: Integrate properly.
|
||||
QHash<QString, int> m_lookupRequests; // FIXME: Integrate properly.
|
||||
QPointer<QWidget> m_alertBox;
|
||||
|
||||
QPointer<BaseTreeView> m_breakView;
|
||||
@@ -2360,9 +2360,10 @@ bool DebuggerEngine::canHandleToolTip(const DebuggerToolTipContext &context) con
|
||||
|
||||
void DebuggerEngine::updateItem(const QString &iname)
|
||||
{
|
||||
if (d->m_lookupRequests.contains(iname)) {
|
||||
showMessage(QString("IGNORING REPEATED REQUEST TO EXPAND " + iname));
|
||||
WatchHandler *handler = watchHandler();
|
||||
const int maxArrayCount = handler->maxArrayCount(iname);
|
||||
if (d->m_lookupRequests.value(iname, -1) == maxArrayCount) {
|
||||
showMessage(QString("IGNORING REPEATED REQUEST TO EXPAND " + iname));
|
||||
WatchItem *item = handler->findItem(iname);
|
||||
QTC_CHECK(item);
|
||||
WatchModelBase *model = handler->model();
|
||||
@@ -2382,7 +2383,7 @@ void DebuggerEngine::updateItem(const QString &iname)
|
||||
}
|
||||
// We could legitimately end up here after expanding + closing + re-expaning an item.
|
||||
}
|
||||
d->m_lookupRequests.insert(iname);
|
||||
d->m_lookupRequests[iname] = maxArrayCount;
|
||||
|
||||
UpdateParameters params;
|
||||
params.partialVariable = iname;
|
||||
|
||||
@@ -2321,7 +2321,6 @@ void QmlEnginePrivate::insertSubItems(WatchItem *parent, const QVariantList &pro
|
||||
QTC_ASSERT(parent, return);
|
||||
LookupItems itemsToLookup;
|
||||
|
||||
const QSet<QString> expandedINames = engine->watchHandler()->expandedINames();
|
||||
for (const QVariant &property : properties) {
|
||||
QmlV8ObjectData propertyData = extractData(property);
|
||||
std::unique_ptr<WatchItem> item(new WatchItem);
|
||||
@@ -2343,7 +2342,7 @@ void QmlEnginePrivate::insertSubItems(WatchItem *parent, const QVariantList &pro
|
||||
item->id = propertyData.handle;
|
||||
item->type = propertyData.type;
|
||||
item->value = propertyData.value.toString();
|
||||
if (item->type.isEmpty() || expandedINames.contains(item->iname))
|
||||
if (item->type.isEmpty() || engine->watchHandler()->isExpandedIName(item->iname))
|
||||
itemsToLookup.insert(propertyData.handle, {item->iname, item->name, item->exp});
|
||||
setWatchItemHasChildren(item.get(), propertyData.hasChildren());
|
||||
parent->appendChild(item.release());
|
||||
|
||||
@@ -212,6 +212,13 @@ public:
|
||||
child->valueEditable = true;
|
||||
item->appendChild(child);
|
||||
}
|
||||
if (childrenElided) {
|
||||
auto child = new WatchItem;
|
||||
child->name = WatchItem::loadMoreName;
|
||||
child->iname = item->iname + "." + WatchItem::loadMoreName;
|
||||
child->wantsChildren = true;
|
||||
item->appendChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
void decode()
|
||||
@@ -260,6 +267,7 @@ public:
|
||||
QString rawData;
|
||||
QString childType;
|
||||
DebuggerEncoding encoding;
|
||||
int childrenElided;
|
||||
quint64 addrbase;
|
||||
quint64 addrstep;
|
||||
Endian endian;
|
||||
@@ -375,6 +383,7 @@ void WatchItem::parseHelper(const GdbMi &input, bool maySort)
|
||||
decoder.item = this;
|
||||
decoder.rawData = mi.data();
|
||||
decoder.childType = input["childtype"].data();
|
||||
decoder.childrenElided = input["childrenelided"].toInt();
|
||||
decoder.addrbase = input["addrbase"].toAddress();
|
||||
decoder.addrstep = input["addrstep"].toAddress();
|
||||
decoder.endian = input["endian"].data() == ">" ? Endian::Big : Endian::Little;
|
||||
@@ -500,6 +509,11 @@ QString WatchItem::toToolTip() const
|
||||
return res;
|
||||
}
|
||||
|
||||
bool WatchItem::isLoadMore() const
|
||||
{
|
||||
return name == loadMoreName;
|
||||
}
|
||||
|
||||
bool WatchItem::isLocal() const
|
||||
{
|
||||
if (arrayIndex >= 0)
|
||||
|
||||
@@ -36,8 +36,10 @@ public:
|
||||
int editType() const;
|
||||
|
||||
static const qint64 InvalidId = -1;
|
||||
constexpr static char loadMoreName[] = "<load more>";
|
||||
|
||||
void setHasChildren(bool c) { wantsChildren = c; }
|
||||
bool isLoadMore() const;
|
||||
|
||||
bool isValid() const { return !iname.isEmpty(); }
|
||||
bool isVTablePointer() const;
|
||||
|
||||
@@ -402,6 +402,7 @@ public:
|
||||
WatchModel(WatchHandler *handler, DebuggerEngine *engine);
|
||||
|
||||
static QString nameForFormat(int format);
|
||||
constexpr static int defaultMaxArrayCount = 100;
|
||||
|
||||
QVariant data(const QModelIndex &idx, int role) const override;
|
||||
bool setData(const QModelIndex &idx, const QVariant &value, int role) override;
|
||||
@@ -410,6 +411,7 @@ public:
|
||||
bool hasChildren(const QModelIndex &idx) const override;
|
||||
bool canFetchMore(const QModelIndex &idx) const override;
|
||||
void fetchMore(const QModelIndex &idx) override;
|
||||
void expand(WatchItem *item, bool requestEngineUpdate);
|
||||
|
||||
QString displayForAutoTest(const QByteArray &iname) const;
|
||||
void reinitialize(bool includeInspectData = false);
|
||||
@@ -468,6 +470,7 @@ public:
|
||||
SeparatedView *m_separatedView; // Not owned.
|
||||
|
||||
QSet<QString> m_expandedINames;
|
||||
QHash<QString, int> m_maxArrayCount;
|
||||
QTimer m_requestUpdateTimer;
|
||||
|
||||
QHash<QString, TypeInfo> m_reportedTypeInfo;
|
||||
@@ -1210,6 +1213,7 @@ bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role
|
||||
if (value.toBool()) {
|
||||
// Should already have been triggered by fetchMore()
|
||||
//QTC_CHECK(m_expandedINames.contains(item->iname));
|
||||
if (!item->isLoadMore())
|
||||
m_expandedINames.insert(item->iname);
|
||||
} else {
|
||||
m_expandedINames.remove(item->iname);
|
||||
@@ -1320,13 +1324,22 @@ bool WatchModel::canFetchMore(const QModelIndex &idx) const
|
||||
|
||||
void WatchModel::fetchMore(const QModelIndex &idx)
|
||||
{
|
||||
if (!idx.isValid())
|
||||
return;
|
||||
if (idx.isValid())
|
||||
expand(nonRootItemForIndex(idx), true);
|
||||
}
|
||||
|
||||
WatchItem *item = nonRootItemForIndex(idx);
|
||||
if (item) {
|
||||
void WatchModel::expand(WatchItem *item, bool requestEngineUpdate)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
if (item->isLoadMore()) {
|
||||
item = item->parent();
|
||||
m_maxArrayCount[item->iname] = m_maxArrayCount.value(item->iname, defaultMaxArrayCount) * 10;
|
||||
if (requestEngineUpdate)
|
||||
m_engine->updateItem(item->iname);
|
||||
} else {
|
||||
m_expandedINames.insert(item->iname);
|
||||
if (item->childCount() == 0)
|
||||
if (requestEngineUpdate && item->childCount() == 0)
|
||||
m_engine->expandItem(item->iname);
|
||||
}
|
||||
}
|
||||
@@ -1749,10 +1762,14 @@ bool WatchModel::contextMenuEvent(const ItemViewEvent &ev)
|
||||
menu->addSeparator();
|
||||
|
||||
addAction(this, menu, Tr::tr("Expand All Children"), item, [this, name = item ? item->iname : QString()] {
|
||||
m_expandedINames.insert(name);
|
||||
if (auto item = findItem(name)) {
|
||||
item->forFirstLevelChildren(
|
||||
[this](WatchItem *child) { m_expandedINames.insert(child->iname); });
|
||||
if (name.isEmpty())
|
||||
return;
|
||||
if (WatchItem *item = findItem(name)) {
|
||||
expand(item, false);
|
||||
item->forFirstLevelChildren([this](WatchItem *child) {
|
||||
if (!child->isLoadMore())
|
||||
expand(child, false);
|
||||
});
|
||||
m_engine->updateLocals();
|
||||
}
|
||||
});
|
||||
@@ -2210,7 +2227,7 @@ bool WatchHandler::insertItem(WatchItem *item)
|
||||
|
||||
void WatchModel::reexpandItems()
|
||||
{
|
||||
for (const QString &iname : std::as_const(m_expandedINames)) {
|
||||
for (const QString &iname: m_expandedINames) {
|
||||
if (WatchItem *item = findItem(iname)) {
|
||||
emit itemIsExpanded(indexForItem(item));
|
||||
emit inameIsExpanded(iname);
|
||||
@@ -2292,7 +2309,8 @@ void WatchHandler::notifyUpdateFinished()
|
||||
m_model->destroyItem(item);
|
||||
|
||||
m_model->forAllItems([this](WatchItem *item) {
|
||||
if (item->wantsChildren && isExpandedIName(item->iname)) {
|
||||
if (item->wantsChildren && isExpandedIName(item->iname)
|
||||
&& item->name != WatchItem::loadMoreName) {
|
||||
m_model->m_engine->showMessage(QString("ADJUSTING CHILD EXPECTATION FOR " + item->iname));
|
||||
item->wantsChildren = false;
|
||||
}
|
||||
@@ -2593,11 +2611,8 @@ const WatchItem *WatchHandler::watchItem(const QModelIndex &idx) const
|
||||
|
||||
void WatchHandler::fetchMore(const QString &iname) const
|
||||
{
|
||||
if (WatchItem *item = m_model->findItem(iname)) {
|
||||
m_model->m_expandedINames.insert(iname);
|
||||
if (item->childCount() == 0)
|
||||
m_model->m_engine->expandItem(iname);
|
||||
}
|
||||
if (WatchItem *item = m_model->findItem(iname))
|
||||
m_model->expand(item, true);
|
||||
}
|
||||
|
||||
WatchItem *WatchHandler::findItem(const QString &iname) const
|
||||
@@ -2711,9 +2726,9 @@ QString WatchHandler::individualFormatRequests() const
|
||||
|
||||
void WatchHandler::appendFormatRequests(DebuggerCommand *cmd) const
|
||||
{
|
||||
QJsonArray expanded;
|
||||
for (const QString &name : std::as_const(m_model->m_expandedINames))
|
||||
expanded.append(name);
|
||||
QJsonObject expanded;
|
||||
for (const QString &iname : std::as_const(m_model->m_expandedINames))
|
||||
expanded.insert(iname, maxArrayCount(iname));
|
||||
|
||||
cmd->arg("expanded", expanded);
|
||||
|
||||
@@ -2817,6 +2832,11 @@ QSet<QString> WatchHandler::expandedINames() const
|
||||
return m_model->m_expandedINames;
|
||||
}
|
||||
|
||||
int WatchHandler::maxArrayCount(const QString &iname) const
|
||||
{
|
||||
return m_model->m_maxArrayCount.value(iname, WatchModel::defaultMaxArrayCount);
|
||||
}
|
||||
|
||||
void WatchHandler::recordTypeInfo(const GdbMi &typeInfo)
|
||||
{
|
||||
if (typeInfo.type() == GdbMi::List) {
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
bool isExpandedIName(const QString &iname) const;
|
||||
QSet<QString> expandedINames() const;
|
||||
int maxArrayCount(const QString &iname) const;
|
||||
|
||||
static QStringList watchedExpressions();
|
||||
static QMap<QString, int> watcherNames();
|
||||
|
||||
@@ -1736,7 +1736,8 @@ void tst_Dumpers::dumper()
|
||||
expandedq.append(',');
|
||||
}
|
||||
expanded += iname;
|
||||
expandedq += '\'' + iname + '\'';
|
||||
expandedq += '\'' + iname + "':";
|
||||
expandedq += data.bigArray ? "10000" : "100";
|
||||
}
|
||||
|
||||
QString exe = m_debuggerBinary;
|
||||
@@ -1769,7 +1770,7 @@ void tst_Dumpers::dumper()
|
||||
"'token':2,'fancy':1,'forcens':1,"
|
||||
"'autoderef':1,'dyntype':1,'passexceptions':1,"
|
||||
"'testing':1,'qobjectnames':1,"
|
||||
"'expanded':[" + expandedq + "]})\n";
|
||||
"'expanded':{" + expandedq + "}})\n";
|
||||
|
||||
cmds += "quit\n";
|
||||
|
||||
@@ -1792,7 +1793,7 @@ void tst_Dumpers::dumper()
|
||||
"'token':2,'fancy':1,'forcens':1,"
|
||||
"'autoderef':1,'dyntype':1,'passexceptions':0,"
|
||||
"'testing':1,'qobjectnames':1,"
|
||||
"'expanded':[" + expandedq + "]})\n"
|
||||
"'expanded':{" + expandedq + "}})\n"
|
||||
"q\n";
|
||||
} else if (m_debuggerEngine == LldbEngine) {
|
||||
QFile fullLldb(t->buildPath + "/lldbcommand.txt");
|
||||
@@ -1808,7 +1809,7 @@ void tst_Dumpers::dumper()
|
||||
"'fancy':1,'forcens':1,"
|
||||
"'autoderef':1,'dyntype':1,'passexceptions':1,"
|
||||
"'testing':1,'qobjectnames':1,"
|
||||
"'expanded':[" + expandedq + "]})\n"
|
||||
"'expanded':{" + expandedq + "}})\n"
|
||||
"quit\n";
|
||||
|
||||
fullLldb.write(cmds.toUtf8());
|
||||
@@ -5310,6 +5311,7 @@ void tst_Dumpers::dumper_data()
|
||||
"&v0, &v1, &v2, &v3, &v4, &v5, &b0, &b1, &b2, &b3")
|
||||
|
||||
+ Cxx11Profile()
|
||||
+ BigArrayProfile()
|
||||
|
||||
+ Check("v0", "<0 items>", "std::valarray<double>")
|
||||
+ Check("v1", "<3 items>", "std::valarray<double>")
|
||||
|
||||
Reference in New Issue
Block a user