From 5bd1d2a0284d90bda180a18f45d48018999fff9b Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <Friedemann.Kleint@nokia.com> Date: Mon, 6 Jul 2009 17:36:50 +0200 Subject: [PATCH] Fix up Debugger helpers to be able to dump QObject's with CDB. - Make container dumper routines set "childnumchild" when known in order to avoid roundtrips; avoid repeated invocations of container.end(). - Completed dumper information in some places to avoid roundtrips. - Extended QVariant helpers by dumpers for common GUI types (rectangles, points, sizes, fonts, size policies). - Introduced artificial QObjectChildList/QObjectProperty types to be able to dump QObject children and properties without using gdb expressions. - Fixed dumping of Signal/Slot list to pass on correct types. Avoid recursions if signal is connected to self. - Replaced expressions by addresses in the dumpers to it make work for CDB. - Reworked dumper test program to have -a, making it usable for tests, add further types. - Gdb: Clear output buffer before calling dumpers, avoiding mixups in case evaluation of expression fails. - Fix the dumper parser used by CDB, do not be fooled by "<synthetic>" addresses, etc. - Pass on a "dumperVersion" in initial query. --- share/qtcreator/gdbmacros/gdbmacros.cpp | 646 ++++++++++++------ share/qtcreator/gdbmacros/test/dumpertest.pro | 2 - share/qtcreator/gdbmacros/test/main.cpp | 173 +++-- src/plugins/debugger/cdb/cdbdumperhelper.cpp | 4 + .../debugger/cdb/cdbstackframecontext.cpp | 50 +- src/plugins/debugger/gdb/gdbengine.cpp | 24 +- src/plugins/debugger/watchhandler.cpp | 3 + src/plugins/debugger/watchutils.cpp | 192 ++++-- src/plugins/debugger/watchutils.h | 5 + 9 files changed, 741 insertions(+), 358 deletions(-) diff --git a/share/qtcreator/gdbmacros/gdbmacros.cpp b/share/qtcreator/gdbmacros/gdbmacros.cpp index 156bfc5e3f2..13017b01baf 100644 --- a/share/qtcreator/gdbmacros/gdbmacros.cpp +++ b/share/qtcreator/gdbmacros/gdbmacros.cpp @@ -46,6 +46,13 @@ #include <QtCore/QString> #include <QtCore/QTextCodec> #include <QtCore/QVector> +#include <QtCore/QTextStream> +#include <QtCore/QPoint> +#include <QtCore/QSize> +#include <QtCore/QRect> +#include <QtCore/QPointF> +#include <QtCore/QSizeF> +#include <QtCore/QRectF> #if QT_VERSION >= 0x040500 #include <QtCore/QSharedPointer> @@ -67,6 +74,10 @@ int qtGhVersion = QT_VERSION; # include <QtGui/QImage> # include <QtGui/QPixmap> # include <QtGui/QWidget> +# include <QtGui/QFont> +# include <QtGui/QColor> +# include <QtGui/QKeySequence> +# include <QtGui/QSizePolicy> #endif #ifdef Q_OS_WIN @@ -445,7 +456,7 @@ struct QDumper explicit QDumper(); ~QDumper(); - // direct write to the output + // direct write to the output QDumper &put(long c); QDumper &put(int i); QDumper &put(double d); @@ -454,6 +465,7 @@ struct QDumper QDumper &put(unsigned int i); QDumper &put(const void *p); QDumper &put(qulonglong c); + QDumper &put(long long c); QDumper &put(const char *str); QDumper &put(const QByteArray &ba); QDumper &put(const QString &str); @@ -578,6 +590,13 @@ QDumper &QDumper::put(unsigned long long c) return *this; } +QDumper &QDumper::put(long long c) +{ + checkFill(); + pos += sprintf(outBuffer + pos, "%lld", c); + return *this; +} + QDumper &QDumper::put(unsigned long c) { checkFill(); @@ -864,7 +883,28 @@ static inline void dumpStdWStringValue(QDumper &d, const std::wstring &str) d.putItem("numchild", "0"); } -static void qDumpInnerValueHelper(QDumper &d, const char *type, const void *addr, +// Tell the calling routine whether a global "childnumchild" attribute makes sense +enum InnerValueResult { InnerValueNotHandled, + InnerValueChildrenSpecified, + InnerValueNoFurtherChildren, + InnerValueFurtherChildren }; + +static inline void dumpChildNumChildren(QDumper &d, InnerValueResult innerValueResult) +{ + switch (innerValueResult) { + case InnerValueChildrenSpecified: + case InnerValueNotHandled: + break; + case InnerValueNoFurtherChildren: + d.putItem("childnumchild", "0"); + break; + case InnerValueFurtherChildren: + d.putItem("childnumchild", "1"); + break; + } +} + +static InnerValueResult qDumpInnerValueHelper(QDumper &d, const char *type, const void *addr, const char *field = "value") { char buf[30]; @@ -872,41 +912,60 @@ static void qDumpInnerValueHelper(QDumper &d, const char *type, const void *addr switch (type[1]) { case 'l': if (isEqual(type, "float")) - d.putItem(field, *(float*)addr); - return; + { d.putItem(field, *(float*)addr); return InnerValueNoFurtherChildren; } + return InnerValueNotHandled; case 'n': - if (isEqual(type, "int")) + if (isEqual(type, "int")) { d.putItem(field, *(int*)addr); - else if (isEqual(type, "unsigned")) + return InnerValueNoFurtherChildren; + } + if (isEqual(type, "unsigned")) { d.putItem(field, *(unsigned int*)addr); - else if (isEqual(type, "unsigned int")) + return InnerValueNoFurtherChildren; + } + if (isEqual(type, "unsigned int")) { d.putItem(field, *(unsigned int*)addr); - else if (isEqual(type, "unsigned long")) + return InnerValueNoFurtherChildren; + } + if (isEqual(type, "unsigned long")) { d.putItem(field, *(unsigned long*)addr); - else if (isEqual(type, "unsigned long long")) + return InnerValueNoFurtherChildren; + } + if (isEqual(type, "unsigned long long")) { d.putItem(field, *(qulonglong*)addr); - return; + return InnerValueNoFurtherChildren; + } + return InnerValueNotHandled; case 'o': - if (isEqual(type, "bool")) + if (isEqual(type, "bool")) { switch (*(bool*)addr) { - case 0: d.putItem(field, "false"); break; - case 1: d.putItem(field, "true"); break; - default: d.putItem(field, *(bool*)addr); break; + case 0: d.putItem(field, "false"); break; + case 1: d.putItem(field, "true"); break; + default: d.putItem(field, *(bool*)addr); break; } - else if (isEqual(type, "double")) + return InnerValueNoFurtherChildren; + } + if (isEqual(type, "double")) { d.putItem(field, *(double*)addr); - else if (isEqual(type, "long")) + return InnerValueNoFurtherChildren; + } + if (isEqual(type, "long")) { d.putItem(field, *(long*)addr); - else if (isEqual(type, "long long")) + return InnerValueNoFurtherChildren; + } + else if (isEqual(type, "long long")) { d.putItem(field, *(qulonglong*)addr); - return; + return InnerValueNoFurtherChildren; + } + return InnerValueNotHandled; case 'B': if (isEqual(type, "QByteArray")) { d.putCommaIfNeeded(); d.put(field).put("encoded=\"1\","); d.putItem(field, *(QByteArray*)addr); + return InnerValueFurtherChildren; } - return; + return InnerValueNotHandled; case 'C': if (isEqual(type, "QChar")) { d.putCommaIfNeeded(); @@ -916,16 +975,18 @@ static void qDumpInnerValueHelper(QDumper &d, const char *type, const void *addr buf[1] = char(c.unicode()); d.putItem(field, buf); d.putItem("numchild", 0); + return InnerValueNoFurtherChildren; } - return; + return InnerValueNotHandled; case 'L': if (startsWith(type, "QList<")) { const QListData *ldata = reinterpret_cast<const QListData*>(addr); d.putItemCount("value", ldata->size()); d.putItem("valuedisabled", "true"); d.putItem("numchild", ldata->size()); + return InnerValueChildrenSpecified; } - return; + return InnerValueNotHandled; case 'O': if (isEqual(type, "QObject *")) { if (addr) { @@ -935,46 +996,55 @@ static void qDumpInnerValueHelper(QDumper &d, const char *type, const void *addr d.putItem("valueencoded", "2"); d.putItem("type", NS"QObject"); d.putItem("displayedtype", ob->metaObject()->className()); + d.putItem("numchild", 1); + return InnerValueChildrenSpecified; } else { d.putItem("value", "0x0"); d.putItem("type", NS"QObject *"); + d.putItem("numchild", 0); + return InnerValueNoFurtherChildren; } } - return; + return InnerValueNotHandled; case 'S': if (isEqual(type, "QString")) { d.putCommaIfNeeded(); d.put(field).put("encoded=\"2\""); d.putItem(field, *(QString*)addr); + return InnerValueNoFurtherChildren; } - return; + return InnerValueNotHandled; case 't': if (isEqual(type, "std::string") || isEqual(type, "std::basic_string<char,std::char_traits<char>,std::allocator<char> >")) { d.putCommaIfNeeded(); dumpStdStringValue(d, *reinterpret_cast<const std::string*>(addr)); - } else if (isEqual(type, "std::wstring") - || isEqual(type, "std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >")) { + return InnerValueNoFurtherChildren; + } + if (isEqual(type, "std::wstring") + || isEqual(type, "std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >")) { dumpStdWStringValue(d, *reinterpret_cast<const std::wstring*>(addr)); + return InnerValueNoFurtherChildren; } - return; + return InnerValueNotHandled; default: - return; + break; } + return InnerValueNotHandled; } -static void qDumpInnerValue(QDumper &d, const char *type, const void *addr) +static InnerValueResult qDumpInnerValue(QDumper &d, const char *type, const void *addr) { d.putItem("addr", addr); d.putItem("type", type); if (!type[0]) - return; + return InnerValueNotHandled; - qDumpInnerValueHelper(d, type, addr); + return qDumpInnerValueHelper(d, type, addr); } -static void qDumpInnerValueOrPointer(QDumper &d, +static InnerValueResult qDumpInnerValueOrPointer(QDumper &d, const char *type, const char *strippedtype, const void *addr) { if (strippedtype) { @@ -982,18 +1052,17 @@ static void qDumpInnerValueOrPointer(QDumper &d, d.putItem("addr", deref(addr)); d.putItem("saddr", deref(addr)); d.putItem("type", strippedtype); - qDumpInnerValueHelper(d, strippedtype, deref(addr)); - } else { - d.putItem("addr", addr); - d.putItem("type", strippedtype); - d.putItem("value", "<null>"); - d.putItem("numchild", "0"); + return qDumpInnerValueHelper(d, strippedtype, deref(addr)); } - } else { d.putItem("addr", addr); - d.putItem("type", type); - qDumpInnerValueHelper(d, type, addr); + d.putItem("type", strippedtype); + d.putItem("value", "<null>"); + d.putItem("numchild", "0"); + return InnerValueChildrenSpecified; } + d.putItem("addr", addr); + d.putItem("type", type); + return qDumpInnerValueHelper(d, type, addr); } ////////////////////////////////////////////////////////////////////////////// @@ -1404,14 +1473,15 @@ static void qDumpQHash(QDumper &d) d.putItemCount("value", n); d.putItem("numchild", n); + if (d.dumpChildren) { if (n > 1000) n = 1000; - bool isSimpleKey = isSimpleType(keyType); - bool isSimpleValue = isSimpleType(valueType); - bool opt = isOptimizedIntKey(keyType); - int keyOffset = hashOffset(opt, true, keySize, valueSize); - int valueOffset = hashOffset(opt, false, keySize, valueSize); + const bool isSimpleKey = isSimpleType(keyType); + const bool isSimpleValue = isSimpleType(valueType); + const bool opt = isOptimizedIntKey(keyType); + const int keyOffset = hashOffset(opt, true, keySize, valueSize); + const int valueOffset = hashOffset(opt, false, keySize, valueSize); d.beginItem("extra"); d.put("isSimpleKey: ").put(isSimpleKey); @@ -1436,10 +1506,7 @@ static void qDumpQHash(QDumper &d) d.putItem("type", valueType); d.putItem("addr", addOffset(node, valueOffset)); } else { - d.beginItem("exp"); - d.put("*('"NS"QHashNode<").put(keyType).put("," - ).put(valueType).put(" >'*)").put(node); - d.endItem(); + d.putItem("addr", node); d.beginItem("type"); d.put("'"NS"QHashNode<").put(keyType).put(",") .put(valueType).put(MAP_NODE_TYPE_END"'"); @@ -1557,6 +1624,7 @@ static void qDumpQList(QDumper &d) d.putItem("valuedisabled", "true"); d.putItem("numchild", n); d.putItem("childtype", d.innertype); + InnerValueResult innerValueResult = InnerValueChildrenSpecified; if (d.dumpChildren) { unsigned innerSize = d.extraInt[0]; bool innerTypeIsPointer = isPointerType(d.innertype); @@ -1583,7 +1651,7 @@ static void qDumpQList(QDumper &d) d.putItem("saddr", p); if (*(void**)p) { //d.putItem("value","@").put(p); - qDumpInnerValue(d, strippedInnerType.data(), deref(p)); + innerValueResult = qDumpInnerValue(d, strippedInnerType.data(), deref(p)); } else { d.putItem("value", "<null>"); d.putItem("numchild", "0"); @@ -1593,11 +1661,11 @@ static void qDumpQList(QDumper &d) if (isInternal) { //qDumpInnerValue(d, d.innertype, p); d.putItem("addr", p); - qDumpInnerValueHelper(d, d.innertype, p); + innerValueResult = qDumpInnerValueHelper(d, d.innertype, p); } else { //qDumpInnerValue(d, d.innertype, deref(p)); d.putItem("addr", deref(p)); - qDumpInnerValueHelper(d, d.innertype, deref(p)); + innerValueResult = qDumpInnerValueHelper(d, d.innertype, deref(p)); } } d.endHash(); @@ -1606,6 +1674,7 @@ static void qDumpQList(QDumper &d) d.putEllipsis(); d.endChildren(); } + dumpChildNumChildren(d, innerValueResult); d.disarm(); } @@ -1624,6 +1693,7 @@ static void qDumpQLinkedList(QDumper &d) d.putItem("valuedisabled", "true"); d.putItem("numchild", n); d.putItem("childtype", d.innertype); + InnerValueResult innerValueResult = InnerValueChildrenSpecified; if (d.dumpChildren) { //unsigned innerSize = d.extraInt[0]; //bool innerTypeIsPointer = isPointerType(d.innertype); @@ -1640,7 +1710,7 @@ static void qDumpQLinkedList(QDumper &d) d.beginHash(); d.putItem("name", i); const void *addr = addOffset(p, 2 * sizeof(void*)); - qDumpInnerValueOrPointer(d, d.innertype, stripped, addr); + innerValueResult = qDumpInnerValueOrPointer(d, d.innertype, stripped, addr); p = deref(p); d.endHash(); } @@ -1648,6 +1718,7 @@ static void qDumpQLinkedList(QDumper &d) d.putEllipsis(); d.endChildren(); } + dumpChildNumChildren(d, innerValueResult); d.disarm(); } @@ -1795,19 +1866,12 @@ static void qDumpQMap(QDumper &d) d.putItem("addr", addOffset(node, valueOffset)); } else { #if QT_VERSION >= 0x040500 + d.putItem("addr", node); // actually, any type (even 'char') will do... d.beginItem("type"); d.put(NS"QMapNode<").put(keyType).put(","); d.put(valueType).put(MAP_NODE_TYPE_END); d.endItem(); - d.beginItem("exp"); - d.put("*('"NS"QMapNode<").put(keyType).put(","); - d.put(valueType).put(" >'*)").put(node); - d.endItem(); - - //d.putItem("exp", "*('"NS"QMapData'*)").put((void*)node); - //d.putItem("exp", "*(char*)").put((void*)node); - // d.putItem("addr", node); does not work as gdb fails to parse #else d.beginItem("type"); d.put(NS"QMapData::Node<").put(keyType).put(","); @@ -1889,14 +1953,12 @@ static void qDumpQObject(QDumper &d) { const QObject *ob = reinterpret_cast<const QObject *>(d.data); const QMetaObject *mo = ob->metaObject(); - unsigned childrenOffset = d.extraInt[0]; d.putItem("value", ob->objectName()); d.putItem("valueencoded", "2"); d.putItem("type", NS"QObject"); d.putItem("displayedtype", mo->className()); d.putItem("numchild", 4); if (d.dumpChildren) { - const QObjectList &children = ob->children(); int slotCount = 0; int signalCount = 0; for (int i = mo->methodCount(); --i >= 0; ) { @@ -1907,61 +1969,33 @@ static void qDumpQObject(QDumper &d) d.beginChildren(); d.beginHash(); d.putItem("name", "properties"); - // FIXME: Note that when simply using '(QObject*)' - // in the cast below, Gdb/MI _sometimes_ misparses - // expressions further down in the tree. - d.beginItem("exp"); - d.put("*(class '"NS"QObject'*)").put(d.data); - d.endItem(); + d.putItem("addr", d.data); d.putItem("type", NS"QObjectPropertyList"); d.putItemCount("value", mo->propertyCount()); d.putItem("numchild", mo->propertyCount()); d.endHash(); -#if 0 - d.beginHash(); - d.putItem("name", "methods"); - d.putItem("exp", "*(class '"NS"QObject'*)").put(d.data); - d.putItemCount("value", mo->methodCount()); - d.putItem("numchild", mo->methodCount()); - d.endHash(); -#endif -#if 0 - d.beginHash(); - d.putItem("name", "senders"); - d.putItem("exp", "(*(class '"NS"ObjectPrivate'*)").put(dfunc(ob)).put(")->senders"); - d.putItem("type", NS"QList<"NS"QObjectPrivateSender>"); - d.endHash(); -#endif d.beginHash(); d.putItem("name", "signals"); - d.beginItem("exp"); - d.put("*(class '"NS"QObject'*)").put(d.data); - d.endItem(); + d.putItem("addr", d.data); d.putItem("type", NS"QObjectSignalList"); d.putItemCount("value", signalCount); d.putItem("numchild", signalCount); d.endHash(); d.beginHash(); d.putItem("name", "slots"); - d.beginItem("exp"); - d.put("*(class '"NS"QObject'*)").put(d.data); - d.endItem(); + d.putItem("addr", d.data); d.putItem("type", NS"QObjectSlotList"); d.putItemCount("value", slotCount); d.putItem("numchild", slotCount); d.endHash(); - if (childrenOffset) { + const QObjectList objectChildren = ob->children(); + if (!objectChildren.empty()) { d.beginHash(); d.putItem("name", "children"); - // works always, but causes additional traffic on the list - //d.putItem("exp", "((class '"NS"QObject'*)").put(d.data).put(")->children()"); - // - //d.putItem("addr", addOffset(dfunc(ob), childrenOffset)); - //d.putItem("type", NS"QList<QObject *>"); - //d.putItemCount("value", children.size()); - qDumpInnerValue(d, NS"QList<"NS"QObject *>", - addOffset(dfunc(ob), childrenOffset)); - d.putItem("numchild", children.size()); + d.putItem("addr", d.data); + d.putItem("type", NS"QObjectChildList"); + d.putItemCount("value", objectChildren.size()); + d.putItem("numchild", objectChildren.size()); d.endHash(); } d.beginHash(); @@ -1981,37 +2015,229 @@ static void qDumpQObject(QDumper &d) d.disarm(); } +#if USE_QT_GUI +static const char *sizePolicyEnumValue(QSizePolicy::Policy p) +{ + switch (p) { + case QSizePolicy::Fixed: + return "Fixed"; + case QSizePolicy::Minimum: + return "Minimum"; + case QSizePolicy::Maximum: + return "Maximum"; + case QSizePolicy::Preferred: + return "Preferred"; + case QSizePolicy::Expanding: + return "Expanding"; + case QSizePolicy::MinimumExpanding: + return "MinimumExpanding"; + case QSizePolicy::Ignored: + break; + } + return "Ignored"; +} + +static QString sizePolicyValue(const QSizePolicy &sp) +{ + QString rc; + QTextStream str(&rc); + // Display as in Designer + str << '[' << sizePolicyEnumValue(sp.horizontalPolicy()) + << ", " << sizePolicyEnumValue(sp.verticalPolicy()) + << ", " << sp.horizontalStretch() << ", " << sp.verticalStretch() << ']'; + return rc; +} +#endif + +static void qDumpQVariantHelper(const QVariant *v, QString *value, + QString *exp, int *numchild) +{ + switch (v->type()) { + case QVariant::Invalid: + *value = QLatin1String("<invalid>"); + *numchild = 0; + break; + case QVariant::String: + *value = QLatin1Char('"') + v->toString() + QLatin1Char('"'); + *numchild = 0; + break; + #if QT_VERSION >= 0x040500 + case QVariant::StringList: + *exp = QString(QLatin1String("(*('"NS"QStringList'*)%1)")) + .arg((quintptr)v); + *numchild = v->toStringList().size(); + break; + #endif + case QVariant::Int: + *value = QString::number(v->toInt()); + *numchild= 0; + break; + case QVariant::Double: + *value = QString::number(v->toDouble()); + *numchild = 0; + break; + case QVariant::Point: { + const QPoint p = v->toPoint(); + *value = QString::fromLatin1("%1, %2").arg(p.x()).arg(p.y()); + } + *numchild = 0; + break; + case QVariant::Size: { + const QSize size = v->toSize(); + *value = QString::fromLatin1("%1x%2").arg(size.width()).arg(size.height()); + } + *numchild = 0; + break; + case QVariant::Rect: { + const QRect rect = v->toRect(); + *value = QString::fromLatin1("%1x%2+%3+%4").arg(rect.width()).arg(rect.height()).arg(rect.x()).arg(rect.y()); + } + *numchild = 0; + break; + case QVariant::PointF: { + const QPointF p = v->toPointF(); + *value = QString::fromLatin1("%1, %2").arg(p.x()).arg(p.y()); + } + *numchild = 0; + break; + + case QVariant::SizeF: { + const QSizeF size = v->toSizeF(); + *value = QString::fromLatin1("%1x%2").arg(size.width()).arg(size.height()); + } + *numchild = 0; + break; + case QVariant::RectF: { + const QRectF rect = v->toRectF(); + *value = QString::fromLatin1("%1x%2+%3+%4").arg(rect.width()).arg(rect.height()).arg(rect.x()).arg(rect.y()); + } + *numchild = 0; + break; +#if USE_QT_GUI + case QVariant::Font: + *value = qvariant_cast<QFont>(*v).toString(); + break; + case QVariant::Color: + *value = qvariant_cast<QColor>(*v).name(); + break; + case QVariant::KeySequence: + *value = qvariant_cast<QKeySequence>(*v).toString(); + break; + case QVariant::SizePolicy: + *value = sizePolicyValue(qvariant_cast<QSizePolicy>(*v)); + break; +#endif + default: { + char buf[1000]; + const char *format = (v->typeName()[0] == 'Q') + ? "'"NS"%s "NS"qVariantValue<"NS"%s >'(*('"NS"QVariant'*)%p)" + : "'%s "NS"qVariantValue<%s >'(*('"NS"QVariant'*)%p)"; + qsnprintf(buf, sizeof(buf) - 1, format, v->typeName(), v->typeName(), v); + *exp = QLatin1String(buf); + *numchild = 1; + break; + } + } +} + +static void qDumpQVariant(QDumper &d, const QVariant *v) +{ + QString value; + QString exp; + int numchild = 0; + qDumpQVariantHelper(v, &value, &exp, &numchild); + bool isInvalid = (v->typeName() == 0); + if (isInvalid) { + d.putItem("value", "(invalid)"); + } else if (value.isEmpty()) { + d.beginItem("value"); + d.put("(").put(v->typeName()).put(") "); + d.endItem(); + } else { + QByteArray ba; + ba += '('; + ba += v->typeName(); + ba += ") "; + ba += qPrintable(value); + d.putItem("value", ba); + d.putItem("valueencoded", "5"); + } + d.putItem("type", NS"QVariant"); + d.putItem("numchild", (isInvalid ? "0" : "1")); + if (d.dumpChildren) { + d.beginChildren(); + d.beginHash(); + d.putItem("name", "value"); + if (!exp.isEmpty()) + d.putItem("exp", qPrintable(exp)); + if (!value.isEmpty()) { + d.putItem("value", value); + d.putItem("valueencoded", "4"); + } + d.putItem("type", v->typeName()); + d.putItem("numchild", numchild); + d.endHash(); + d.endChildren(); + } + d.disarm(); +} + +static inline void qDumpQVariant(QDumper &d) +{ + qDumpQVariant(d, reinterpret_cast<const QVariant *>(d.data)); +} + +static void qDumpQObjectProperty(QDumper &d) +{ + const QObject *ob = (const QObject *)d.data; + // extract "local.Object.property" + QString iname = d.iname; + const int dotPos = iname.lastIndexOf(QLatin1Char('.')); + if (dotPos == -1) + return; + iname.remove(0, dotPos + 1); + const QVariant v = ob->property(iname.toAscii().constData()); + qDumpQVariant(d, &v); + d.disarm(); +} + static void qDumpQObjectPropertyList(QDumper &d) { const QObject *ob = (const QObject *)d.data; const QMetaObject *mo = ob->metaObject(); + const int propertyCount = mo->propertyCount(); d.putItem("addr", "<synthetic>"); d.putItem("type", NS"QObjectPropertyList"); - d.putItem("numchild", mo->propertyCount()); + d.putItem("numchild", propertyCount); + d.putItemCount("value", propertyCount); if (d.dumpChildren) { d.beginChildren(); - for (int i = mo->propertyCount(); --i >= 0; ) { + for (int i = propertyCount; --i >= 0; ) { const QMetaProperty & prop = mo->property(i); d.beginHash(); d.putItem("name", prop.name()); - d.beginItem("exp"); - d.put("((").put(mo->className()).put("*)"); - d.put(ob).put(")->").put(prop.name()).put("()"); - d.endItem(); - if (isEqual(prop.typeName(), "QString")) { + switch (prop.type()) { + case QVariant::String: + d.putItem("type", prop.typeName()); d.putItem("value", prop.read(ob).toString()); d.putItem("valueencoded", "2"); - d.putItem("type", NS"QString"); d.putItem("numchild", "0"); - } else if (isEqual(prop.typeName(), "bool")) { + break; + case QVariant::Bool: + d.putItem("type", prop.typeName()); d.putItem("value", (prop.read(ob).toBool() ? "true" : "false")); d.putItem("numchild", "0"); - } else if (isEqual(prop.typeName(), "int")) { + break; + case QVariant::Int: d.putItem("value", prop.read(ob).toInt()); d.putItem("numchild", "0"); + break; + default: + d.putItem("addr", d.data); + d.putItem("type", NS"QObjectProperty"); + d.putItem("numchild", "1"); + break; } - d.putItem("type", prop.typeName()); - d.putItem("numchild", "1"); d.endHash(); } d.endChildren(); @@ -2074,6 +2300,29 @@ static const ConnectionList &qConnectionList(const QObject *ob, int signalNumber } #endif +// Write party involved in a slot/signal element, +// avoid to recursion to self. +static inline void qDumpQObjectConnectionPart(QDumper &d, + const QObject *owner, + const QObject *partner, + int number, const char *namePostfix) +{ + d.beginHash(); + d.beginItem("name"); + d.put(number).put(namePostfix); + d.endItem(); + if (partner == owner) { + d.putItem("value", "<this>"); + d.putItem("valueencoded", "2"); + d.putItem("type", owner->metaObject()->className()); + d.putItem("numchild", 0); + d.putItem("addr", owner); + } else { + qDumpInnerValueHelper(d, NS"QObject *", partner); + } + d.endHash(); +} + static void qDumpQObjectSignal(QDumper &d) { unsigned signalNumber = d.extraInt[0]; @@ -2089,12 +2338,7 @@ static void qDumpQObjectSignal(QDumper &d) const ConnectionList &connList = qConnectionList(ob, signalNumber); for (int i = 0; i != connList.size(); ++i) { const Connection &conn = connectionAt(connList, i); - d.beginHash(); - d.beginItem("name"); - d.put(i).put(" receiver"); - d.endItem(); - qDumpInnerValueHelper(d, NS"QObject *", conn.receiver); - d.endHash(); + qDumpQObjectConnectionPart(d, ob, conn.receiver, i, " receiver"); d.beginHash(); d.beginItem("name"); d.put(i).put(" slot"); @@ -2129,14 +2373,17 @@ static void qDumpQObjectSignalList(QDumper &d) const QObject *ob = reinterpret_cast<const QObject *>(d.data); const QMetaObject *mo = ob->metaObject(); int count = 0; - for (int i = mo->methodCount(); --i >= 0; ) + const int methodCount = mo->methodCount(); + for (int i = methodCount; --i >= 0; ) count += (mo->method(i).methodType() == QMetaMethod::Signal); + d.putItem("type", "QObjectSignalList"); + d.putItemCount("value", count); d.putItem("addr", d.data); d.putItem("numchild", count); #if QT_VERSION >= 0x040400 if (d.dumpChildren) { d.beginChildren(); - for (int i = 0; i != mo->methodCount(); ++i) { + for (int i = 0; i != methodCount; ++i) { const QMetaMethod & method = mo->method(i); if (method.methodType() == QMetaMethod::Signal) { int k = mo->indexOfSignal(method.signature()); @@ -2145,10 +2392,7 @@ static void qDumpQObjectSignalList(QDumper &d) d.putItem("name", k); d.putItem("value", method.signature()); d.putItem("numchild", connList.size()); - //d.putItem("numchild", "1"); - d.beginItem("exp"); - d.put("*(class '"NS"QObject'*)").put(d.data); - d.endItem(); + d.putItem("addr", d.data); d.putItem("type", NS"QObjectSignal"); d.endHash(); } @@ -2182,12 +2426,7 @@ static void qDumpQObjectSlot(QDumper &d) if (conn.receiver == ob && conn.method == slotNumber) { ++numchild; const QMetaMethod &method = sender->metaObject()->method(signal); - d.beginHash(); - d.beginItem("name"); - d.put(s).put(" sender"); - d.endItem(); - qDumpInnerValueHelper(d, NS"QObject *", sender); - d.endHash(); + qDumpQObjectConnectionPart(d, ob, sender, s, " sender"); d.beginHash(); d.beginItem("name"); d.put(s).put(" signal"); @@ -2226,14 +2465,17 @@ static void qDumpQObjectSlotList(QDumper &d) const QMetaObject *mo = ob->metaObject(); int count = 0; - for (int i = mo->methodCount(); --i >= 0; ) + const int methodCount = mo->methodCount(); + for (int i = methodCount; --i >= 0; ) count += (mo->method(i).methodType() == QMetaMethod::Slot); d.putItem("numchild", count); + d.putItemCount("value", count); + d.putItem("type", NS"QObjectSlotList"); if (d.dumpChildren) { d.beginChildren(); #if QT_VERSION >= 0x040400 - for (int i = 0; i != mo->methodCount(); ++i) { + for (int i = 0; i != methodCount; ++i) { const QMetaMethod & method = mo->method(i); if (method.methodType() == QMetaMethod::Slot) { d.beginHash(); @@ -2254,9 +2496,7 @@ static void qDumpQObjectSlotList(QDumper &d) } } d.putItem("numchild", numchild); - d.beginItem("exp"); - d.put("*(class '"NS"QObject'*)").put(d.data); - d.endItem(); + d.putItem("addr", d.data); d.putItem("type", NS"QObjectSlot"); d.endHash(); } @@ -2267,6 +2507,27 @@ static void qDumpQObjectSlotList(QDumper &d) d.disarm(); } +static void qDumpQObjectChildList(QDumper &d) +{ + const QObject *ob = reinterpret_cast<const QObject *>(d.data); + const QObjectList children = ob->children(); + const int size = children.size(); + + d.putItem("numchild", size); + d.putItemCount("value", size); + d.putItem("type", NS"QObjectChildList"); + if (d.dumpChildren) { + d.beginChildren(); + for (int i = 0; i != size; ++i) { + d.beginHash(); + d.putItem("name", i); + qDumpInnerValueHelper(d, NS"QObject *", children.at(i)); + d.endHash(); + } + d.endChildren(); + } + d.disarm(); +} #if USE_QT_GUI static void qDumpQPixmap(QDumper &d) @@ -2442,89 +2703,6 @@ static void qDumpQTextCodec(QDumper &d) d.disarm(); } -static void qDumpQVariantHelper(const void *data, QString *value, - QString *exp, int *numchild) -{ - const QVariant &v = *reinterpret_cast<const QVariant *>(data); - switch (v.type()) { - case QVariant::Invalid: - *value = QLatin1String("<invalid>"); - *numchild = 0; - break; - case QVariant::String: - *value = QLatin1Char('"') + v.toString() + QLatin1Char('"'); - *numchild = 0; - break; - #if QT_VERSION >= 0x040500 - case QVariant::StringList: - *exp = QString(QLatin1String("(*('"NS"QStringList'*)%1)")) - .arg((quintptr)data); - *numchild = v.toStringList().size(); - break; - #endif - case QVariant::Int: - *value = QString::number(v.toInt()); - *numchild= 0; - break; - case QVariant::Double: - *value = QString::number(v.toDouble()); - *numchild = 0; - break; - default: { - char buf[1000]; - const char *format = (v.typeName()[0] == 'Q') - ? "'"NS"%s "NS"qVariantValue<"NS"%s >'(*('"NS"QVariant'*)%p)" - : "'%s "NS"qVariantValue<%s >'(*('"NS"QVariant'*)%p)"; - qsnprintf(buf, sizeof(buf) - 1, format, v.typeName(), v.typeName(), data); - *exp = QLatin1String(buf); - *numchild = 1; - break; - } - } -} - -static void qDumpQVariant(QDumper &d) -{ - const QVariant &v = *reinterpret_cast<const QVariant *>(d.data); - QString value; - QString exp; - int numchild = 0; - qDumpQVariantHelper(d.data, &value, &exp, &numchild); - bool isInvalid = (v.typeName() == 0); - if (isInvalid) { - d.putItem("value", "(invalid)"); - } else if (value.isEmpty()) { - d.beginItem("value"); - d.put("(").put(v.typeName()).put(") "); - d.endItem(); - } else { - QByteArray ba; - ba += '('; - ba += v.typeName(); - ba += ") "; - ba += qPrintable(value); - d.putItem("value", ba); - d.putItem("valueencoded", "5"); - } - d.putItem("type", NS"QVariant"); - d.putItem("numchild", (isInvalid ? "0" : "1")); - if (d.dumpChildren) { - d.beginChildren(); - d.beginHash(); - d.putItem("name", "value"); - if (!exp.isEmpty()) - d.putItem("exp", qPrintable(exp)); - if (!value.isEmpty()) { - d.putItem("value", value); - d.putItem("valueencoded", "4"); - } - d.putItem("type", v.typeName()); - d.putItem("numchild", numchild); - d.endHash(); - d.endChildren(); - } - d.disarm(); -} static void qDumpQVector(QDumper &d) { @@ -2547,6 +2725,7 @@ static void qDumpQVector(QDumper &d) d.putItemCount("value", n); d.putItem("valuedisabled", "true"); d.putItem("numchild", n); + InnerValueResult innerValueResult = InnerValueChildrenSpecified; if (d.dumpChildren) { QByteArray strippedInnerType = stripPointerType(d.innertype); const char *stripped = @@ -2557,7 +2736,7 @@ static void qDumpQVector(QDumper &d) for (int i = 0; i != n; ++i) { d.beginHash(); d.putItem("name", i); - qDumpInnerValueOrPointer(d, d.innertype, stripped, + innerValueResult = qDumpInnerValueOrPointer(d, d.innertype, stripped, addOffset(v, i * innersize + typeddatasize)); d.endHash(); } @@ -2565,6 +2744,7 @@ static void qDumpQVector(QDumper &d) d.putEllipsis(); d.endChildren(); } + dumpChildNumChildren(d, innerValueResult); d.disarm(); } @@ -2633,7 +2813,8 @@ static void qDumpStdList(QDumper &d) #endif int nn = 0; std::list<int>::const_iterator it = list.begin(); - for (; nn < 101 && it != list.end(); ++nn, ++it) + const std::list<int>::const_iterator cend = list.end(); + for (; nn < 101 && it != cend; ++nn, ++it) qCheckAccess(it.operator->()); if (nn > 100) @@ -2643,22 +2824,24 @@ static void qDumpStdList(QDumper &d) d.putItem("numchild", nn); d.putItem("valuedisabled", "true"); + InnerValueResult innerValueResult = InnerValueChildrenSpecified; if (d.dumpChildren) { QByteArray strippedInnerType = stripPointerType(d.innertype); const char *stripped = isPointerType(d.innertype) ? strippedInnerType.data() : 0; d.beginChildren(); it = list.begin(); - for (int i = 0; i < 1000 && it != list.end(); ++i, ++it) { + for (int i = 0; i < 1000 && it != cend; ++i, ++it) { d.beginHash(); d.putItem("name", i); - qDumpInnerValueOrPointer(d, d.innertype, stripped, it.operator->()); + innerValueResult = qDumpInnerValueOrPointer(d, d.innertype, stripped, it.operator->()); d.endHash(); } if (it != list.end()) d.putEllipsis(); d.endChildren(); } + dumpChildNumChildren(d, innerValueResult); d.disarm(); } @@ -2744,7 +2927,8 @@ static void qDumpStdSet(QDumper &d) if (nn < 0) return; DummyType::const_iterator it = set.begin(); - for (int i = 0; i < nn && i < 10 && it != set.end(); ++i, ++it) + const DummyType::const_iterator cend = set.end(); + for (int i = 0; i < nn && i < 10 && it != cend; ++i, ++it) qCheckAccess(it.operator->()); d.putItemCount("value", nn); @@ -2752,6 +2936,7 @@ static void qDumpStdSet(QDumper &d) d.putItem("numchild", nn); d.putItem("valueoffset", d.extraInt[0]); + InnerValueResult innerValueResult = InnerValueChildrenSpecified; if (d.dumpChildren) { int valueOffset = 0; // d.extraInt[0]; QByteArray strippedInnerType = stripPointerType(d.innertype); @@ -2764,17 +2949,18 @@ static void qDumpStdSet(QDumper &d) d.beginChildren(); it = set.begin(); - for (int i = 0; i < 1000 && it != set.end(); ++i, ++it) { + for (int i = 0; i < 1000 && it != cend; ++i, ++it) { const void *node = it.operator->(); d.beginHash(); d.putItem("name", i); - qDumpInnerValueOrPointer(d, d.innertype, stripped, node); + innerValueResult = qDumpInnerValueOrPointer(d, d.innertype, stripped, node); d.endHash(); } if (it != set.end()) d.putEllipsis(); d.endChildren(); } + dumpChildNumChildren(d, innerValueResult); d.disarm(); } @@ -2838,6 +3024,7 @@ static void qDumpStdVector(QDumper &d) d.putItemCount("value", n); d.putItem("valuedisabled", "true"); d.putItem("numchild", n); + InnerValueResult innerValueResult = InnerValueChildrenSpecified; if (d.dumpChildren) { unsigned innersize = d.extraInt[0]; QByteArray strippedInnerType = stripPointerType(d.innertype); @@ -2857,6 +3044,7 @@ static void qDumpStdVector(QDumper &d) d.putEllipsis(); d.endChildren(); } + dumpChildNumChildren(d, innerValueResult); d.disarm(); } @@ -2974,6 +3162,8 @@ static void handleProtocolVersion2and3(QDumper & d) qDumpQObject(d); else if (isEqual(type, "QObjectPropertyList")) qDumpQObjectPropertyList(d); + else if (isEqual(type, "QObjectProperty")) + qDumpQObjectProperty(d); else if (isEqual(type, "QObjectMethodList")) qDumpQObjectMethodList(d); else if (isEqual(type, "QObjectSignal")) @@ -2984,6 +3174,8 @@ static void handleProtocolVersion2and3(QDumper & d) qDumpQObjectSlot(d); else if (isEqual(type, "QObjectSlotList")) qDumpQObjectSlotList(d); + else if (isEqual(type, "QObjectChildList")) + qDumpQObjectChildList(d); break; case 'P': #if USE_QT_GUI @@ -3125,11 +3317,13 @@ void *qDumpObjectData440( "\""NS"QModelIndex\"," "\""NS"QObject\"," "\""NS"QObjectMethodList\"," // hack to get nested properties display + "\""NS"QObjectProperty\"," "\""NS"QObjectPropertyList\"," "\""NS"QObjectSignal\"," "\""NS"QObjectSignalList\"," "\""NS"QObjectSlot\"," "\""NS"QObjectSlotList\"," + "\""NS"QObjectChildList\"," //"\""NS"QRegion\"," "\""NS"QSet\"," "\""NS"QString\"," @@ -3166,7 +3360,7 @@ void *qDumpObjectData440( "\"").put(((QT_VERSION >> 16) & 255)).put("\"," "\"").put(((QT_VERSION >> 8) & 255)).put("\"," "\"").put(((QT_VERSION) & 255)).put("\"]"); - d.put(",namespace=\""NS"\","); + d.put(",dumperversion=\"1.3\","); // Dump out size information d.put("sizes={"); d.put("int=\"").put(sizeof(int)).put("\",") diff --git a/share/qtcreator/gdbmacros/test/dumpertest.pro b/share/qtcreator/gdbmacros/test/dumpertest.pro index 7b5fd3e582a..ddac946a12a 100644 --- a/share/qtcreator/gdbmacros/test/dumpertest.pro +++ b/share/qtcreator/gdbmacros/test/dumpertest.pro @@ -4,8 +4,6 @@ # #------------------------------------------------- -QT -= gui - TARGET = dumpertest CONFIG += console CONFIG -= app_bundle diff --git a/share/qtcreator/gdbmacros/test/main.cpp b/share/qtcreator/gdbmacros/test/main.cpp index eca00156df3..0ec31578b0e 100644 --- a/share/qtcreator/gdbmacros/test/main.cpp +++ b/share/qtcreator/gdbmacros/test/main.cpp @@ -32,6 +32,8 @@ #include <QtCore/QSharedPointer> #include <QtCore/QTimer> #include <QtCore/QMap> +#include <QtCore/QVariant> +#include <QtGui/QAction> #include <string> #include <list> @@ -174,6 +176,20 @@ static int dumpQMapIntString() return 0; } +static int dumpQVariant() +{ + QVariant test(QLatin1String("item")); + prepareInBuffer("QVariant", "local.qvariant", "local.qvariant", ""); + qDumpObjectData440(2, 42, testAddress(&test), 1, 0, 0,0 ,0); + fputs(qDumpOutBuffer, stdout); + fputs("\n\n", stdout); + test = QVariant(QStringList(QLatin1String("item1"))); + prepareInBuffer("QVariant", "local.qvariant", "local.qvariant", ""); + qDumpObjectData440(2, 42, testAddress(&test), 1, 0, 0,0 ,0); + fputs(qDumpOutBuffer, stdout); + return 0; +} + // --------------- std types static int dumpStdString() @@ -309,76 +325,127 @@ static int dumpStdMapIntString() static int dumpQObject() { // Requires the childOffset to be know, but that is not critical - QTimer t; + QAction action(0); + QObject x; + QAction *a2= new QAction(&action); + a2->setObjectName(QLatin1String("a2")); + action.setObjectName(QLatin1String("action")); + QObject::connect(&action, SIGNAL(triggered()), &x, SLOT(deleteLater())); prepareInBuffer("QObject", "local.qobject", "local.qobject", ""); - qDumpObjectData440(2, 42, testAddress(&t), 1, 0, 0, 0, 0); + qDumpObjectData440(2, 42, testAddress(&action), 1, 0, 0, 0, 0); + fputs(qDumpOutBuffer, stdout); + fputs("\n\n", stdout); + // Property list + prepareInBuffer("QObjectPropertyList", "local.qobjectpropertylist", "local.qobjectpropertylist", ""); + qDumpObjectData440(2, 42, testAddress(&action), 1, 0, 0, 0, 0); + fputs(qDumpOutBuffer, stdout); + fputs("\n\n", stdout); + // Signal list + prepareInBuffer("QObjectSignalList", "local.qobjectsignallist", "local.qobjectsignallist", ""); + qDumpObjectData440(2, 42, testAddress(&action), 1, 0, 0, 0, 0); + fputs(qDumpOutBuffer, stdout); + // Slot list + prepareInBuffer("QObjectSlotList", "local.qobjectslotlist", "local.qobjectslotlist", ""); + qDumpObjectData440(2, 42, testAddress(&action), 1, 0, 0, 0, 0); + fputs(qDumpOutBuffer, stdout); + fputs("\n\n", stdout); + // Signal list + prepareInBuffer("QObjectChildList", "local.qobjectchildlist", "local.qobjectchildlist", ""); + qDumpObjectData440(2, 42, testAddress(&action), 1, 0, 0, 0, 0); + fputs(qDumpOutBuffer, stdout); + return 0; +} + +static int dumpQObjectList() +{ + // Requires the childOffset to be know, but that is not critical + QObject *root = new QObject; + root ->setObjectName("root"); + QTimer *t1 = new QTimer; + t1 ->setObjectName("t1"); + QTimer *t2 = new QTimer; + t2 ->setObjectName("t2"); + QObjectList test; + test << root << t1 << t2; + prepareInBuffer("QList", "local.qobjectlist", "local.qobjectlist", "QObject *"); + qDumpObjectData440(2, 42, testAddress(&test), sizeof(QObject*), 0, 0, 0, 0); fputs(qDumpOutBuffer, stdout); fputc('\n', stdout); + delete root; return 0; } -static bool dumpType(const char *arg) +typedef int (*DumpFunction)(); +typedef QMap<QString, DumpFunction> TypeDumpFunctionMap; + +static TypeDumpFunctionMap registerTypes() { - if (!qstrcmp(arg, "QString")) - { dumpQString(); return true; } - if (!qstrcmp(arg, "QSharedPointer<QString>")) - { dumpQSharedPointerQString(); return true; } - if (!qstrcmp(arg, "QStringList")) - { dumpQStringList(); return true; } - if (!qstrcmp(arg, "QList<int>")) - { dumpQIntList(); return true; } - if (!qstrcmp(arg, "QList<std::string>")) - { dumpStdStringQList(); return true; } - if (!qstrcmp(arg, "QVector<int>")) - { dumpQIntVector(); return true; } - if (!qstrcmp(arg, "QMap<int,QString>")) - { dumpQMapIntString(); return true; } - if (!qstrcmp(arg, "QMap<int,int>")) - { dumpQMapIntInt(); return true; } - if (!qstrcmp(arg, "string")) - { dumpStdString(); return true; } - if (!qstrcmp(arg, "wstring")) - { dumpStdWString(); return true; } - if (!qstrcmp(arg, "list<int>")) - { dumpStdIntList(); return true; } - if (!qstrcmp(arg, "list<string>")) - { dumpStdStringList(); return true; } - if (!qstrcmp(arg, "vector<int>")) - { dumpStdIntVector(); return true; } - if (!qstrcmp(arg, "vector<string>")) - { dumpStdStringVector(); return true; } - if (!qstrcmp(arg, "vector<wstring>")) - { dumpStdWStringVector(); return true; } - if (!qstrcmp(arg, "set<int>")) - { dumpStdIntSet(); return true; } - if (!qstrcmp(arg, "set<string>")) - { dumpStdStringSet(); return true; } - if (!qstrcmp(arg, "map<int,string>")) - { dumpStdMapIntString(); return true; } - if (!qstrcmp(arg, "QObject")) - { dumpQObject(); return true; } - return false; + TypeDumpFunctionMap rc; + rc.insert("QString", dumpQString); + rc.insert("QSharedPointer<QString>", dumpQSharedPointerQString); + rc.insert("QStringList", dumpQStringList); + rc.insert("QList<int>", dumpQIntList); + rc.insert("QList<std::string>", dumpStdStringQList); + rc.insert("QVector<int>", dumpQIntVector); + rc.insert("QMap<int,QString>", dumpQMapIntString); + rc.insert("QMap<int,int>", dumpQMapIntInt); + rc.insert("string", dumpStdString); + rc.insert("wstring", dumpStdWString); + rc.insert("list<int>", dumpStdIntList); + rc.insert("list<string>", dumpStdStringList); + rc.insert("vector<int>", dumpStdIntVector); + rc.insert("vector<string>", dumpStdStringVector); + rc.insert("vector<wstring>", dumpStdWStringVector); + rc.insert("set<int>", dumpStdIntSet); + rc.insert("set<string>", dumpStdStringSet); + rc.insert("map<int,string>", dumpStdMapIntString); + rc.insert("QObject", dumpQObject); + rc.insert("QObjectList", dumpQObjectList); + rc.insert("QVariant", dumpQVariant); + return rc; } int main(int argc, char *argv[]) { + printf("\nQt Creator Debugging Helper testing tool\n\n"); printf("Running query protocol\n"); qDumpObjectData440(1, 42, 0, 1, 0, 0, 0, 0); fputs(qDumpOutBuffer, stdout); fputc('\n', stdout); fputc('\n', stdout); - if (argc < 2) + + const TypeDumpFunctionMap tdm = registerTypes(); + const TypeDumpFunctionMap::const_iterator cend = tdm.constEnd(); + + if (argc < 2) { + printf("Usage: %s [-a]|<type1> <type2..>\n", argv[0]); + printf("Supported types: "); + for (TypeDumpFunctionMap::const_iterator it = tdm.constBegin(); it != cend; ++it) { + fputs(qPrintable(it.key()), stdout); + fputc(' ', stdout); + } + fputc('\n', stdout); return 0; - for (int i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "-u")) { - optTestUninitialized = true; - printf("\nTesting uninitialized...\n"); - continue; + } + + int rc = 0; + if (argc == 2 && !qstrcmp(argv[1], "-a")) { + for (TypeDumpFunctionMap::const_iterator it = tdm.constBegin(); it != cend; ++it) { + printf("\nTesting: %s\n", qPrintable(it.key())); + rc += (*it.value())(); + } + } else { + for (int i = 1; i < argc; i++) { + const char *arg = argv[i]; + printf("\nTesting: %s\n", arg); + const TypeDumpFunctionMap::const_iterator it = tdm.constFind(QLatin1String(arg)); + if (it == cend) { + rc = -1; + fprintf(stderr, "\nUnhandled type: %s\n", argv[i]); + } else { + rc = (*it.value())(); + } } - printf("\nTesting %s\n", arg); - if (!dumpType(arg)) - printf("\nUnhandled type: %s\n", arg); } - return 0; + return rc; } diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp index bedf02dd41e..b4cd0bff6c7 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.cpp +++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp @@ -561,6 +561,10 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool *errorMessage = msgNotHandled(wd.type); return DumpNotHandled; } + if (wd.addr.isEmpty()) { + *errorMessage = QString::fromLatin1("Adress is missing for '%1' (%2).").arg(wd.exp, wd.type); + return DumpNotHandled; + } // Ensure types are parsed and known. if (!ensureInitialized(errorMessage)) { diff --git a/src/plugins/debugger/cdb/cdbstackframecontext.cpp b/src/plugins/debugger/cdb/cdbstackframecontext.cpp index 870ba8f262f..f68beb0db3c 100644 --- a/src/plugins/debugger/cdb/cdbstackframecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstackframecontext.cpp @@ -35,6 +35,7 @@ #include "watchhandler.h" #include <QtCore/QDebug> +#include <QtCore/QCoreApplication> namespace Debugger { namespace Internal { @@ -164,6 +165,46 @@ bool WatchHandleDumperInserter::expandPointerToDumpable(const WatchData &wd, QSt return handled; } +// When querying an item, the queried item is sometimes returned in incomplete form. +// Take over values from source. +static inline void fixDumperResult(const WatchData &source, + QList<WatchData> *result, + bool suppressGrandChildren) +{ + const int size = result->size(); + if (!size) + return; + WatchData &returned = result->front(); + if (returned.iname != source.iname) + return; + if (returned.type.isEmpty()) + returned.setType(source.type); + if (returned.isValueNeeded()) { + if (source.isValueKnown()) { + returned.setValue(source.value); + } else { + // Should not happen + returned.setValue(QCoreApplication::translate("CdbStackFrameContext", "<Unknown>")); + } + } + if (size == 1) + return; + // Fix the children: If the address is missing, we cannot query any further. + const QList<WatchData>::iterator wend = result->end(); + QList<WatchData>::iterator it = result->begin(); + for (++it; it != wend; ++it) { + WatchData &wd = *it; + if (wd.addr.isEmpty() && wd.isSomethingNeeded()) { + wd.setAllUnneeded(); + } else { + // Hack: Suppress endless recursion of the model. To be fixed, + // the model should not query non-visible items. + if (suppressGrandChildren && (wd.isChildrenNeeded() || wd.isHasChildrenNeeded())) + wd.setHasChildren(false); + } + } +} + WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd) { if (debugCDBWatchHandling) @@ -180,9 +221,7 @@ WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd) if (debugCDBWatchHandling) qDebug() << "dumper triggered"; // Dumpers omit types for complicated templates - if (!m_dumperResult.isEmpty() && m_dumperResult.front().type.isEmpty() - && m_dumperResult.front().iname == wd.iname) - m_dumperResult.front().setType(wd.type); + fixDumperResult(wd, &m_dumperResult, false); // Discard the original item and insert the dumper results foreach(const WatchData &dwd, m_dumperResult) m_wh->insertData(dwd); @@ -254,12 +293,17 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal, QList<WatchData> dumperResult; const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(incompleteLocal, true, OwnerDumper, &dumperResult, errorMessage); if (dr == CdbDumperHelper::DumpOk) { + // Hack to stop endless model recursion + const bool suppressGrandChildren = !wh->isExpandedIName(incompleteLocal.iname); + fixDumperResult(incompleteLocal, &dumperResult, suppressGrandChildren); foreach(const WatchData &dwd, dumperResult) wh->insertData(dwd); } else { const QString msg = QString::fromLatin1("Unable to further expand dumper watch data: '%1' (%2): %3/%4").arg(incompleteLocal.name, incompleteLocal.type).arg(int(dr)).arg(*errorMessage); qWarning("%s", qPrintable(msg)); WatchData wd = incompleteLocal; + if (wd.isValueNeeded()) + wd.setValue(QCoreApplication::translate("CdbStackFrameContext", "<Unknown>")); wd.setAllUnneeded(); wh->insertData(wd); } diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index b564a5468a7..597b12ebd19 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -3192,24 +3192,36 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record, const //qDebug() << m_availableSimpleDebuggingHelpers << "DATA DUMPERS AVAILABLE"; } -void GdbEngine::sendWatchParameters(const QByteArray ¶ms0) +static inline QString arrayFillCommand(const char *array, const QByteArray ¶ms) { - QByteArray params = params0; - params.append('\0'); char buf[50]; - sprintf(buf, "set {char[%d]} &qDumpInBuffer = {", params.size()); + sprintf(buf, "set {char[%d]} &%s = {", params.size(), array); QByteArray encoded; encoded.append(buf); - for (int i = 0; i != params.size(); ++i) { + const int size = params.size(); + for (int i = 0; i != size; ++i) { sprintf(buf, "%d,", int(params[i])); encoded.append(buf); } encoded[encoded.size() - 1] = '}'; + return _(encoded); +} + +void GdbEngine::sendWatchParameters(const QByteArray ¶ms0) +{ + QByteArray params = params0; + params.append('\0'); + const QString inBufferCmd = arrayFillCommand("qDumpInBuffer", params); params.replace('\0','!'); emit gdbInputAvailable(LogMisc, QString::fromUtf8(params)); - postCommand(_(encoded)); + params.clear(); + params.append('\0'); + const QString outBufferCmd = arrayFillCommand("qDumpOutBuffer", params); + + postCommand(inBufferCmd); + postCommand(outBufferCmd); } void GdbEngine::handleVarAssign(const GdbResultRecord &, const QVariant &) diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 30eb77478b0..a5e188ed3c9 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -209,6 +209,8 @@ QString WatchData::toString() const QTextStream str(&res); if (!iname.isEmpty()) str << "iname=\"" << iname << doubleQuoteComma; + if (!addr.isEmpty()) + str << "addr=\"" << addr << doubleQuoteComma; if (!exp.isEmpty()) str << "exp=\"" << exp << doubleQuoteComma; @@ -780,6 +782,7 @@ static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *i void WatchModel::insertData(const WatchData &data) { + // qDebug() << "WMI:" << data.toString(); QTC_ASSERT(!data.iname.isEmpty(), return); WatchItem *parent = findItem(parentName(data.iname), m_root); if (!parent) { diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp index 5ef9523ed45..555b6ac6cd4 100644 --- a/src/plugins/debugger/watchutils.cpp +++ b/src/plugins/debugger/watchutils.cpp @@ -470,16 +470,19 @@ QString cppExpressionAt(TextEditor::ITextEditor *editor, int pos, QtDumperResult::Child::Child() : keyEncoded(0), valueEncoded(0), - childCount(0), - valuedisabled(false) + childCount(-1), + valuedisabled(false), + valueEncountered(false) { } QtDumperResult::QtDumperResult() : + valueEncountered(false), valueEncoded(0), valuedisabled(false), - childCount(0), - internal(false) + childCount(-1), + internal(false), + childChildCount(-1) { } @@ -488,15 +491,17 @@ void QtDumperResult::clear() iname.clear(); value.clear(); address.clear(); + addressInfo.clear(); type.clear(); extra.clear(); displayedType.clear(); valueEncoded = 0; - valuedisabled = false; - childCount = 0; + valueEncountered = valuedisabled = false; + childCount = -1; internal = false; childType.clear(); children.clear(); + childChildCount = -1; } QList<WatchData> QtDumperResult::toWatchData(int source) const @@ -508,45 +513,70 @@ QList<WatchData> QtDumperResult::toWatchData(int source) const const QChar dot = QLatin1Char('.'); const int lastDotIndex = root.iname.lastIndexOf(dot); root.exp = root.name = lastDotIndex == -1 ? iname : iname.mid(lastDotIndex + 1); - root.setValue(decodeData(value, valueEncoded)); + if (valueEncountered) { + root.setValue(decodeData(value, valueEncoded)); + root.valuedisabled = valuedisabled; + } root.setType(displayedType.isEmpty() ? type : displayedType); - root.valuedisabled = valuedisabled; root.setAddress(address); root.source = source; - root.setHasChildren(childCount > 0); - // Children - if (childCount > 0) { - if (children.size() == childCount) { - for (int c = 0; c < childCount; c++) { - const Child &dchild = children.at(c); - rc.push_back(WatchData()); - WatchData &wchild = rc.back(); - wchild.source = source; - wchild.iname = iname; - wchild.iname += dot; - wchild.iname += dchild.name; - // Use key entry as name (which is used for map nodes) - if (dchild.key.isEmpty()) { - wchild.name = dchild.name; - } else { - wchild.name = decodeData(dchild.key, dchild.keyEncoded); - if (wchild.name.size() > 13) { - wchild.name.truncate(12); - wchild.name += QLatin1String("..."); - } + if (childCount >= 0) + root.setHasChildren(childCount > 0); + // Children. Sanity check after parsing sets childcount to list size + // if list is not empty + if (children.empty()) { + if (childCount > 0) + root.setChildrenNeeded(); + } else { + root.setChildrenUnneeded(); + for (int c = 0; c < childCount; c++) { + const Child &dchild = children.at(c); + rc.push_back(WatchData()); + WatchData &wchild = rc.back(); + wchild.source = source; + wchild.iname = iname; + wchild.iname += dot; + wchild.iname += dchild.name; + // Use key entry as name (which is used for map nodes) + if (dchild.key.isEmpty()) { + wchild.name = dchild.name; + } else { + wchild.name = decodeData(dchild.key, dchild.keyEncoded); + if (wchild.name.size() > 13) { + wchild.name.truncate(12); + wchild.name += QLatin1String("..."); } - wchild.exp = dchild.exp; + } + wchild.exp = dchild.exp; + if (dchild.valueEncountered) { wchild.valuedisabled = dchild.valuedisabled; - wchild.setType(dchild.type.isEmpty() ? childType : dchild.type); - wchild.setAddress(dchild.address); wchild.setValue(decodeData(dchild.value, dchild.valueEncoded)); + } + wchild.setType(dchild.type.isEmpty() ? childType : dchild.type); + wchild.setAddress(dchild.address); + // Child overrides. + const int effectiveChildChildCount = dchild.childCount == -1 ? childChildCount : dchild.childCount; + switch (effectiveChildChildCount) { + case -1: + wchild.setChildrenNeeded(); + wchild.setHasChildrenNeeded(); + break; + case 0: wchild.setHasChildren(false); + break; + default: + wchild.setHasChildren(true); + break; } - root.setChildrenUnneeded(); - } else { - root.setChildrenNeeded(); } } + if (debug) { + QDebug nospace = qDebug().nospace(); + nospace << "QtDumperResult::toWatchData" << *this << '\n'; + foreach(const WatchData &wd, rc) + nospace << " " << wd.toString() << '\n'; + } + return rc; } @@ -554,11 +584,20 @@ QDebug operator<<(QDebug in, const QtDumperResult &d) { QDebug nospace = in.nospace(); nospace << " iname=" << d.iname << " type=" << d.type << " displayed=" << d.displayedType - << " address=" << d.address - << " value=" << d.value - << " disabled=" << d.valuedisabled - << " encoded=" << d.valueEncoded << " internal=" << d.internal + << " address=" << d.address; + if (!d.addressInfo.isEmpty()) + nospace << " addressInfo=" << d.addressInfo; + if (d.valueEncountered) { + nospace << " encoded=" << d.valueEncoded + << " value=" << d.value + << " disabled=" << d.valuedisabled; + } else { + nospace << " <no value>"; + } + nospace << " childnumchild=" << d.childChildCount + << " internal=" << d.internal << " extra='" << d.extra << "'\n"; + const int realChildCount = d.children.size(); if (d.childCount || realChildCount) { nospace << "childCount=" << d.childCount << '/' << realChildCount @@ -571,8 +610,12 @@ QDebug operator<<(QDebug in, const QtDumperResult &d) << " name=" << c.name; if (!c.key.isEmpty()) nospace << " keyencoded=" << c.keyEncoded << " key=" << c.key; - nospace << " valueencoded=" << c.valueEncoded << " value=" << c.value - << "childcount=" << c.childCount << '\n'; + if (c.valueEncountered) { + nospace << " valueencoded=" << c.valueEncoded << " value=" << c.value; + } else { + nospace << " <no value>"; + } + nospace << "childcount=" << c.childCount << '\n'; } } return in; @@ -608,6 +651,7 @@ void QtDumperHelper::clear() m_sizeCache.clear(); qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0); m_expressionCache.clear(); + m_dumperVersion.clear(); } static inline void formatQtVersion(int v, QTextStream &str) @@ -622,7 +666,7 @@ QString QtDumperHelper::toString(bool debug) const QTextStream str(&rc); str << "version="; formatQtVersion(m_qtVersion, str); - str << " namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types <type enum>: "; + str << "dumperversion='" << m_dumperVersion << "' namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types <type enum>: "; const NameTypeMap::const_iterator cend = m_nameTypeMap.constEnd(); for (NameTypeMap::const_iterator it = m_nameTypeMap.constBegin(); it != cend; ++it) { str <<",[" << it.key() << ',' << it.value() << ']'; @@ -639,9 +683,9 @@ QString QtDumperHelper::toString(bool debug) const } const QString nameSpace = m_qtNamespace.isEmpty() ? QCoreApplication::translate("QtDumperHelper", "<none>") : m_qtNamespace; return QCoreApplication::translate("QtDumperHelper", - "%n known types, Qt version: %1, Qt namespace: %2", + "%n known types, Qt version: %1, Qt namespace: %2 Dumper version: %3", 0, QCoreApplication::CodecForTr, - m_nameTypeMap.size()).arg(qtVersionString(), nameSpace); + m_nameTypeMap.size()).arg(qtVersionString(), nameSpace, m_dumperVersion); } QtDumperHelper::Type QtDumperHelper::simpleType(const QString &simpleType) const @@ -726,10 +770,9 @@ QtDumperHelper::Type QtDumperHelper::specialType(QString s) QtDumperHelper::ExpressionRequirement QtDumperHelper::expressionRequirements(Type t) { + switch (t) { case QAbstractItemType: - case QObjectSlotType: - case QObjectSignalType: case QVectorType: case StdMapType: return NeedsComplexExpression; @@ -738,6 +781,7 @@ QtDumperHelper::ExpressionRequirement QtDumperHelper::expressionRequirements(Typ case QMapNodeType: return NeedsCachedExpression; default: + // QObjectSlotType, QObjectSignalType need the signal number, which is numeric break; } return NeedsNoExpression; @@ -968,9 +1012,9 @@ public: explicit QueryDumperParser(const char *s); struct Data { - Data() : qtVersion(0) {} QString qtNameSpace; QString qtVersion; + QString dumperVersion; QStringList types; QList<SizeEntry> sizes; QMap<QString, QString> expressionCache; @@ -986,7 +1030,7 @@ protected: virtual bool handleValue(const char *k, int size); private: - enum Mode { None, ExpectingDumpers, ExpectingVersion, + enum Mode { None, ExpectingDumpers, ExpectingQtVersion, ExpectingDumperVersion, ExpectingNameSpace, ExpectingSizes, ExpectingExpressionCache }; Mode m_mode; Data m_data; @@ -1017,7 +1061,11 @@ bool QueryDumperParser::handleKeyword(const char *k, int size) return true; } if (!qstrncmp(k, "qtversion", size)) { - m_mode = ExpectingVersion; + m_mode = ExpectingQtVersion; + return true; + } + if (!qstrncmp(k, "dumperversion", size)) { + m_mode = ExpectingDumperVersion; return true; } if (!qstrncmp(k, "namespace", size)) { @@ -1038,7 +1086,7 @@ bool QueryDumperParser::handleKeyword(const char *k, int size) bool QueryDumperParser::handleListStart() { - return m_mode == ExpectingDumpers || m_mode == ExpectingVersion; + return m_mode == ExpectingDumpers || m_mode == ExpectingQtVersion; } bool QueryDumperParser::handleListEnd() @@ -1064,7 +1112,10 @@ bool QueryDumperParser::handleValue(const char *k, int size) case ExpectingNameSpace: m_data.qtNameSpace = QString::fromLatin1(k, size); break; - case ExpectingVersion: // ["4","1","5"] + case ExpectingDumperVersion: + m_data.dumperVersion = QString::fromLatin1(k, size); + break; + case ExpectingQtVersion: // ["4","1","5"] if (!m_data.qtVersion.isEmpty()) m_data.qtVersion += QLatin1Char('.'); m_data.qtVersion += QString::fromLatin1(k, size); @@ -1092,6 +1143,7 @@ bool QtDumperHelper::parseQuery(const char *data, Debugger debugger) foreach (const QueryDumperParser::SizeEntry &se, parser.data().sizes) addSize(se.first, se.second); m_expressionCache = parser.data().expressionCache; + m_dumperVersion = parser.data().dumperVersion; return true; } @@ -1248,17 +1300,6 @@ void QtDumperHelper::evaluationParameters(const WatchData &data, case QAbstractItemType: inner = data.addr.mid(1); break; - case QObjectType: - case QWidgetType: - if (debugger == GdbDebugger) { - extraArgs[0] = QLatin1String("(char*)&((('"); - extraArgs[0] += m_qtNamespace; - extraArgs[0] += QLatin1String("QObjectPrivate'*)&"); - extraArgs[0] += data.exp; - extraArgs[0] += QLatin1String(")->children)-(char*)&"); - extraArgs[0] += data.exp; - } - break; case QVectorType: extraArgs[1] = QLatin1String("(char*)&(("); extraArgs[1] += data.exp; @@ -1355,6 +1396,8 @@ void QtDumperHelper::evaluationParameters(const WatchData &data, qWarning("Unknown type encountered in %s.\n", Q_FUNC_INFO); break; case SupportedType: + case QObjectType: + case QWidgetType: break; } @@ -1412,6 +1455,7 @@ private: ExpectingType, ExpectingDisplayedType, ExpectingInternal, ExpectingValueDisabled, ExpectingValueEncoded, ExpectingCommonChildType, ExpectingChildCount, + ExpectingChildChildOverrideCount, ExpectingExtra, IgnoreNext, ChildModeStart, @@ -1488,7 +1532,7 @@ ValueDumperParser::Mode ValueDumperParser::nextMode(Mode in, const char *keyword if (!qstrncmp(keyword, "displayedtype", size)) return ExpectingDisplayedType; if (!qstrncmp(keyword, "childnumchild", size)) - return IgnoreNextChildMode; + return ExpectingChildChildOverrideCount; break; } return in > ChildModeStart ? IgnoreNextChildMode : IgnoreNext; @@ -1519,10 +1563,17 @@ bool ValueDumperParser::handleValue(const char *k, int size) case ExpectingIName: m_result.iname = QString::fromLatin1(valueBA); break; - case ExpectingAddress: - m_result.address = QString::fromLatin1(valueBA); - break; + case ExpectingAddress: { + const QString address = QString::fromLatin1(valueBA); + if (address.startsWith(QLatin1String("0x"))) { + m_result.address = address; + } else { + m_result.addressInfo = address; + } + } + break; case ExpectingValue: + m_result.valueEncountered = true; m_result.value = valueBA; break; case ExpectingValueDisabled: @@ -1549,6 +1600,9 @@ bool ValueDumperParser::handleValue(const char *k, int size) case ExpectingChildCount: m_result.childCount = QString::fromLatin1(valueBA).toInt(); break; + case ExpectingChildChildOverrideCount: + m_result.childChildCount = QString::fromLatin1(valueBA).toInt(); + break; case ExpectingChildren: case IgnoreNextChildMode: case IgnoreNext: @@ -1566,6 +1620,7 @@ bool ValueDumperParser::handleValue(const char *k, int size) m_result.children.back().key = valueBA; break; case ExpectingChildValue: + m_result.children.back().valueEncountered = true; m_result.children.back().value = valueBA; break; case ExpectingChildExpression: @@ -1590,12 +1645,13 @@ bool ValueDumperParser::handleValue(const char *k, int size) bool QtDumperHelper::parseValue(const char *data, QtDumperResult *r) { ValueDumperParser parser(data); + if (!parser.run()) return false; *r = parser.result(); // Sanity - if (r->childCount < r->children.size()) - r->childCount = r->children.size(); + if (!r->children.empty() && r->childCount != r->children.size()) + r->childCount = r->children.size(); if (debug) qDebug() << '\n' << data << '\n' << *r; return true; diff --git a/src/plugins/debugger/watchutils.h b/src/plugins/debugger/watchutils.h index 9d39372aa0e..d1d9b05215f 100644 --- a/src/plugins/debugger/watchutils.h +++ b/src/plugins/debugger/watchutils.h @@ -99,6 +99,7 @@ struct QtDumperResult QString exp; QString type; QByteArray key; + bool valueEncountered; QByteArray value; }; @@ -108,15 +109,18 @@ struct QtDumperResult QString iname; QString address; + QString addressInfo; // "<synthetic>" or such, in the 2nd adress field. QString type; QString extra; QString displayedType; + bool valueEncountered; QByteArray value; int valueEncoded; bool valuedisabled; int childCount; bool internal; QString childType; + int childChildCount; QList <Child> children; }; @@ -242,6 +246,7 @@ private: QMap<QString, QString> m_expressionCache; int m_qtVersion; + QString m_dumperVersion; QString m_qtNamespace; }; -- GitLab