From eb75f40a9883b437f1e1ce722a769a450a905d39 Mon Sep 17 00:00:00 2001 From: hjk <qtc-committer@nokia.com> Date: Fri, 25 Jun 2010 09:07:14 +0200 Subject: [PATCH] debugger: allow per item selection of display method in Locals&Watchers --- share/qtcreator/gdbmacros/dumper.py | 135 +++++++++++++++---------- share/qtcreator/gdbmacros/gdbmacros.py | 16 +-- src/plugins/debugger/gdb/gdbengine.cpp | 12 ++- src/plugins/debugger/watchhandler.cpp | 30 ++++-- src/plugins/debugger/watchhandler.h | 12 +-- src/plugins/debugger/watchwindow.cpp | 15 +-- 6 files changed, 135 insertions(+), 85 deletions(-) diff --git a/share/qtcreator/gdbmacros/dumper.py b/share/qtcreator/gdbmacros/dumper.py index 3657b3e03cd..2be8128bd95 100644 --- a/share/qtcreator/gdbmacros/dumper.py +++ b/share/qtcreator/gdbmacros/dumper.py @@ -771,6 +771,49 @@ class Item: self.name = name +####################################################################### +# +# SetupCommand +# +####################################################################### + +# This is a mapping from 'type name' to 'display alternatives'. + +qqDumpers = {} +qqFormats = {} + + +class SetupCommand(gdb.Command): + """Setup Creator Pretty Printing""" + + def __init__(self): + super(SetupCommand, self).__init__("bbsetup", gdb.COMMAND_OBSCURE) + + def invoke(self, args, from_tty): + module = sys.modules[__name__] + for key, value in module.__dict__.items(): + if key.startswith("qdump__"): + name = key[7:] + qqDumpers[name] = value + elif key.startswith("qform__"): + name = key[7:] + formats = "" + try: + formats = value() + except: + pass + qqFormats[name] = formats + result = "dumpers=[" + # Too early: ns = qtNamespace() + for key, value in qqFormats.items(): + result += '{type="%s",formats="%s"},' % (key, value) + result += ']' + #result += '],namespace="%s"' % ns + print(result) + +SetupCommand() + + ####################################################################### # # FrameCommand @@ -778,7 +821,7 @@ class Item: ####################################################################### class FrameCommand(gdb.Command): - """Do fancy stuff. Usage bb --verbose expandedINames""" + """Do fancy stuff.""" def __init__(self): super(FrameCommand, self).__init__("bb", gdb.COMMAND_OBSCURE) @@ -846,7 +889,6 @@ class Dumper: self.currentValueEncoding = None self.currentType = None self.currentTypePriority = -100 - self.dumpers = "" self.typeformats = {} self.formats = {} self.expandedINames = "" @@ -890,28 +932,6 @@ class Dumper: #warn("VARIABLES: %s" % varList) #warn("EXPANDED INAMES: %s" % self.expandedINames) module = sys.modules[__name__] - self.dumpers = {} - - if False: - for key, value in module.__dict__.items(): - if key.startswith("qdump__"): - self.dumpers += '"' + key[7:] + '",' - output = "dumpers=[%s]," % self.dumpers - #output += "qtversion=[%d,%d,%d]" - #output += "qtversion=[4,6,0]," - output += "namespace=\"%s\"," % qtNamespace() - output += "dumperversion=\"2.0\"," - output += "sizes=[]," - output += "expressions=[]" - output += "]" - print output - return - - - if self.useFancy: - for key, value in module.__dict__.items(): - if key.startswith("qdump__"): - self.dumpers[key[7:]] = value # # Locals @@ -1184,7 +1204,7 @@ class Dumper: self.putNumChild(0) def itemFormat(self, item): - format = self.formats.get(str(cleanAddress(item.value.address))) + format = self.formats.get(item.iname) if format is None: format = self.typeformats.get(stripClassTag(str(item.value.type))) return format @@ -1224,6 +1244,7 @@ class Dumper: value = item.value type = value.type + format = self.itemFormat(item) if type.code == gdb.TYPE_CODE_REF: try: @@ -1243,62 +1264,66 @@ class Dumper: type.strip_typedefs().unqualified()).replace("::", "__") #warn(" STRIPPED: %s" % strippedType) - #warn(" DUMPERS: %s" % self.dumpers) - #warn(" DUMPERS: %s" % (strippedType in self.dumpers)) + #warn(" DUMPERS: %s" % (strippedType in qqDumpers)) if isSimpleType(type.unqualified()): #warn("IS SIMPLE: %s " % type) + #self.putAddress(value.address) self.putType(item.value.type) self.putValue(value) self.putNumChild(0) - elif strippedType in self.dumpers: + elif ((format is None) or (format >= 1)) and strippedType in qqDumpers: #warn("IS DUMPABLE: %s " % type) + #self.putAddress(value.address) self.putType(item.value.type) - self.dumpers[strippedType](self, item) + qqDumpers[strippedType](self, item) #warn(" RESULT: %s " % self.output) elif type.code == gdb.TYPE_CODE_ENUM: #warn("GENERIC ENUM: %s" % value) + #self.putAddress(value.address) self.putType(item.value.type) self.putValue(value) self.putNumChild(0) elif type.code == gdb.TYPE_CODE_PTR: + warn("POINTER: %s" % format) isHandled = False - format = self.itemFormat(item) - if not format is None: self.putAddress(value.address) self.putType(item.value.type) - self.putNumChild(0) isHandled = True + if format == 0: + # Bald pointer. + self.putPointerValue(value.address) + self.putNumChild(1) + elif format == 1 or format == 2: + # Latin1 or UTF-8 + f = select(format == 1, Hex2EncodedLatin1, Hex2EncodedUtf8) + self.putValue(encodeCharArray(value, 100), f) + self.putNumChild(0) + elif format == 3: + # UTF-16. + self.putValue(encodeChar2Array(value, 100), Hex4EncodedBigEndian) + self.putNumChild(0) + elif format == 4: + # UCS-4: + self.putValue(encodeChar4Array(value, 100), Hex8EncodedBigEndian) + self.putNumChild(0) - if format == 0: - # Bald pointer. - self.putPointerValue(value.address) - elif format == 1 or format == 2: - # Latin1 or UTF-8 - f = select(format == 1, Hex2EncodedLatin1, Hex2EncodedUtf8) - self.putValue(encodeCharArray(value, 100), f) - elif format == 3: - # UTF-16. - self.putValue(encodeChar2Array(value, 100), Hex4EncodedBigEndian) - elif format == 4: - # UCS-4: - self.putValue(encodeChar4Array(value, 100), Hex8EncodedBigEndian) - - strippedType = str(type.strip_typedefs()) \ - .replace("(anonymous namespace)", "") - if (not isHandled) and strippedType.find("(") != -1: - # A function pointer. - self.putValue(str(item.value)) - self.putAddress(value.address) - self.putType(item.value.type) - self.putNumChild(0) - isHandled = True + if (not isHandled): + strippedType = str(type.strip_typedefs()) \ + .replace("(anonymous namespace)", "") + if strippedType.find("(") != -1: + # A function pointer. + self.putValue(str(item.value)) + self.putAddress(value.address) + self.putType(item.value.type) + self.putNumChild(0) + isHandled = True if (not isHandled) and self.useFancy: if isNull(value): diff --git a/share/qtcreator/gdbmacros/gdbmacros.py b/share/qtcreator/gdbmacros/gdbmacros.py index 8296d8e4da9..d675d9e1db6 100644 --- a/share/qtcreator/gdbmacros/gdbmacros.py +++ b/share/qtcreator/gdbmacros/gdbmacros.py @@ -66,7 +66,7 @@ def qdump__QAbstractItem(d, item): rr = call(m, "rowCount(child)") cc = call(m, "columnCount(child)") d.putNumChild(rr * cc) - d.putField("value", + d.putValue( call(m, "data(child, Qt::DisplayRole).toString())"), 6) #with SubItem(d): @@ -424,6 +424,8 @@ def qdump__QList(d, item): d.putItem(Item(pp, item.iname, i)) p += 1 +def qdump__QImage(): + return "Normal,Displayed"; def qdump__QImage(d, item): painters = item.value["painters"] @@ -436,7 +438,6 @@ def qdump__QImage(d, item): d.putValue("(%dx%d)" % (d_ptr["width"], d_ptr["height"])) bits = d_ptr["data"] nbytes = d_ptr["nbytes"] - d.putField("typeformats", "Normal,Displayed"); d.putNumChild(0) #d.putNumChild(1) if d.isExpanded(item): @@ -447,9 +448,9 @@ def qdump__QImage(d, item): d.putNumChild(0) d.putValue("size: %s bytes" % nbytes); format = d.itemFormat(item) - if format == 0: + if format == 1: d.putDisplay(StopDisplay) - elif format == 1: + elif format == 2: if False: # Take four bytes at a time, this is critical for performance. # In fact, even four at a time is too slow beyond 100x100 or so. @@ -1458,15 +1459,16 @@ def qdump__QSizeF(d, item): def qdump__QStack(d, item): qdump__QVector(d, item) +def qform__QString(): + return "Inline,Separate Window"; def qdump__QString(d, item): d.putStringValue(item.value) d.putNumChild(0) - d.putField("typeformats", "Normal,Displayed"); format = d.itemFormat(item) - if format == 0: + if format == 1: d.putDisplay(StopDisplay) - elif format == 1: + elif format == 2: d.putField("editformat", 2) str = encodeString(item.value) d.putField("editvalue", str) diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index d6f75bade4e..b63d3632fb3 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -1525,6 +1525,16 @@ void GdbEngine::handleHasPython(const GdbResponse &response) { if (response.resultClass == GdbResultDone) { m_hasPython = true; + GdbMi contents = response.data.findChild("consolestreamoutput"); + GdbMi data; + data.fromStringMultiple(contents.data()); + const GdbMi dumpers = data.findChild("dumpers"); + foreach (const GdbMi &dumper, dumpers.children()) { + QString type = _(dumper.findChild("type").data()); + QStringList formats(tr("Raw structure")); + formats.append(_(dumper.findChild("formats").data()).split(_(","))); + watchHandler()->addTypeFormats(type, formats); + } } else { m_hasPython = false; if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperLoadedByGdbPreload @@ -4125,7 +4135,7 @@ bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QStr "\"python execfile('" + dumperSourcePath + "gdbmacros.py')\"", NonCriticalResponse); - postCommand("-interpreter-exec console \"help bb\"", + postCommand("-interpreter-exec console \"bbsetup\"", CB(handleHasPython)); return true; diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 92b71279f61..ddf6f9e0238 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -613,7 +613,8 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const return QVariant(QLatin1Char('*') + item->parent->name); return data.name; case 1: { - int format = m_handler->m_individualFormats.value(data.addr, -1); + int format = + m_handler->m_individualFormats.value(data.iname, -1); if (format == -1) format = m_handler->m_typeFormats.value(data.type, -1); return truncateValue(formattedValue(data, format)); @@ -650,26 +651,30 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const case LocalsExpandedRole: return m_handler->m_expandedINames.contains(data.iname); - case LocalsTypeFormatListRole: - if (!data.typeFormats.isEmpty()) - return data.typeFormats.split(','); + case LocalsTypeFormatListRole: { if (isIntType(data.type)) return QStringList() << tr("decimal") << tr("hexadecimal") << tr("binary") << tr("octal"); if (data.type.endsWith(QLatin1Char('*'))) return QStringList() - << tr("Bald pointer") + << tr("Raw pointer") << tr("Latin1 string") << tr("UTF8 string") << tr("UTF16 string") << tr("UCS4 string"); - break; + // Hack: Compensate for namespaces. + QString type = data.type; + int pos = type.indexOf("::Q"); + if (pos >= 0 && type.count(':') == 2) + type = type.mid(pos + 2); + return m_handler->m_reportedTypeFormats.value(type); + } case LocalsTypeFormatRole: return m_handler->m_typeFormats.value(data.type, -1); case LocalsIndividualFormatRole: - return m_handler->m_individualFormats.value(data.addr, -1); + return m_handler->m_individualFormats.value(data.iname, -1); case LocalsRawValueRole: return data.value; @@ -772,9 +777,9 @@ bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int ro case LocalsIndividualFormatRole: { const int format = value.toInt(); if (format == -1) { - m_handler->m_individualFormats.remove(data.addr); + m_handler->m_individualFormats.remove(data.iname); } else { - m_handler->m_individualFormats[data.addr] = format; + m_handler->m_individualFormats[data.iname] = format; } engine()->updateWatchData(data); break; @@ -1503,7 +1508,7 @@ int WatchHandler::format(const QByteArray &iname) const { int result = -1; if (const WatchData *item = findItem(iname)) { - int result = m_individualFormats.value(iname, -1); + int result = m_individualFormats.value(item->iname, -1); if (result == -1) result = m_typeFormats.value(item->type, -1); } @@ -1561,5 +1566,10 @@ QByteArray WatchHandler::individualFormatRequests() const return ba; } +void WatchHandler::addTypeFormats(const QString &type, const QStringList &formats) +{ + m_reportedTypeFormats.insert(type, formats); +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h index a973b884930..0ba82d25d6e 100644 --- a/src/plugins/debugger/watchhandler.h +++ b/src/plugins/debugger/watchhandler.h @@ -36,12 +36,8 @@ #include <QtCore/QObject> #include <QtCore/QHash> #include <QtCore/QSet> +#include <QtCore/QStringList> #include <QtCore/QAbstractItemModel> -#include <QtScript/QScriptValue> - -QT_BEGIN_NAMESPACE -class QDebug; -QT_END_NAMESPACE namespace Debugger { namespace Internal { @@ -49,6 +45,7 @@ namespace Internal { class DebuggerEngine; class WatchItem; class WatchHandler; +class WatchData; enum WatchType { @@ -174,6 +171,8 @@ public: static QString watcherEditPlaceHolder(); int format(const QByteArray &iname) const; + void addTypeFormats(const QString &type, const QStringList &formats); + private: friend class WatchModel; @@ -195,7 +194,8 @@ private: QHash<QByteArray, int> m_watcherNames; QByteArray watcherName(const QByteArray &exp); QHash<QString, int> m_typeFormats; - QHash<QByteArray, int> m_individualFormats; + QHash<QByteArray, int> m_individualFormats; // Indexed by iname. + QHash<QString, QStringList> m_reportedTypeFormats; // Items expanded in the Locals & Watchers view. QSet<QByteArray> m_expandedINames; diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp index 17783f8ee4a..67357efccd4 100644 --- a/src/plugins/debugger/watchwindow.cpp +++ b/src/plugins/debugger/watchwindow.cpp @@ -231,7 +231,7 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) if (alternativeFormats.isEmpty()) { typeFormatMenu.setEnabled(false); } else { - clearTypeFormatAction = typeFormatMenu.addAction(tr("Clear")); + clearTypeFormatAction = typeFormatMenu.addAction(tr("Automatic")); clearTypeFormatAction->setEnabled(typeFormat != -1); clearTypeFormatAction->setCheckable(true); clearTypeFormatAction->setChecked(typeFormat == -1); @@ -254,13 +254,14 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) QMenu individualFormatMenu; QList<QAction *> individualFormatActions; QAction *clearIndividualFormatAction = 0; - if (idx.isValid() && address) { + if (idx.isValid()) { individualFormatMenu.setTitle( - tr("Change Format for Object at 0x%1").arg(address, 0, 16)); + tr("Change Format for Object Named \"%1\"").arg(mi0.data().toString())); if (alternativeFormats.isEmpty()) { individualFormatMenu.setEnabled(false); } else { - clearIndividualFormatAction = individualFormatMenu.addAction(tr("Clear")); + clearIndividualFormatAction + = individualFormatMenu.addAction(tr("Automatic")); clearIndividualFormatAction->setEnabled(individualFormat != -1); clearIndividualFormatAction->setCheckable(true); clearIndividualFormatAction->setChecked(individualFormat == -1); @@ -302,10 +303,12 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) if (canShowMemory && address) actOpenMemoryEditAtVariableAddress = - new QAction(tr("Open Memory Editor at Object's Address (0x%1)").arg(address, 0, 16), &menu); + new QAction(tr("Open Memory Editor at Object's Address (0x%1)") + .arg(address, 0, 16), &menu); if (createPointerActions) actOpenMemoryEditAtPointerValue = - new QAction(tr("Open Memory Editor at Referenced Address (0x%1)").arg(pointerValue, 0, 16), &menu); + new QAction(tr("Open Memory Editor at Referenced Address (0x%1)") + .arg(pointerValue, 0, 16), &menu); menu.addSeparator(); QAction *actSetWatchPointAtVariableAddress = 0; -- GitLab