From 6e93f4544d1f9b0fafc3f0d80a3f7049533fa8c0 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <Friedemann.Kleint@nokia.com> Date: Thu, 2 Jul 2009 16:38:15 +0200 Subject: [PATCH] Display Q<>Pointers/Work towards displaying maps/QMaps in CDB. Make dumpers pass on more size information initially, namely the various Q<>Pointers. Introduce enum for those special template types whose size does not vary with the arguments (Q<>Pointer, std::allocators...) to make it more easily extensible. Pass on some common QMapNode<> sizes as well. Introduce an expression cache to the common QtDumperHelper class and make dumpers pass some common expression values (value offsets of common QMapNode<> incarnations). Make CDBDumperHelper use the expression cache. Extend dumper tester. --- share/qtcreator/gdbmacros/gdbmacros.cpp | 57 ++++++- share/qtcreator/gdbmacros/test/main.cpp | 127 ++++++++++---- src/plugins/debugger/cdb/cdbdumperhelper.cpp | 11 +- src/plugins/debugger/cdb/cdbdumperhelper.h | 4 +- .../debugger/cdb/cdbstackframecontext.cpp | 25 ++- src/plugins/debugger/watchutils.cpp | 155 +++++++++++++----- src/plugins/debugger/watchutils.h | 28 +++- 7 files changed, 316 insertions(+), 91 deletions(-) diff --git a/share/qtcreator/gdbmacros/gdbmacros.cpp b/share/qtcreator/gdbmacros/gdbmacros.cpp index 03840ac854d..29b6feed765 100644 --- a/share/qtcreator/gdbmacros/gdbmacros.cpp +++ b/share/qtcreator/gdbmacros/gdbmacros.cpp @@ -50,6 +50,7 @@ #if QT_VERSION >= 0x040500 #include <QtCore/QSharedPointer> #include <QtCore/QSharedDataPointer> +#include <QtCore/QSharedData> #include <QtCore/QWeakPointer> #endif @@ -1345,6 +1346,11 @@ int hashOffset(bool optimizedIntKey, bool forKey, unsigned keySize, unsigned val } } +#ifdef Q_CC_MSVC +# define MAP_NODE_TYPE_END ">" +#else +# define MAP_NODE_TYPE_END " >" +#endif static void qDumpQHash(QDumper &d) { @@ -1407,7 +1413,7 @@ static void qDumpQHash(QDumper &d) d.endItem(); d.beginItem("type"); d.put("'"NS"QHashNode<").put(keyType).put(",") - .put(valueType).put(" >'"); + .put(valueType).put(MAP_NODE_TYPE_END"'"); d.endItem(); } d.endHash(); @@ -1763,7 +1769,7 @@ static void qDumpQMap(QDumper &d) // actually, any type (even 'char') will do... d.beginItem("type"); d.put(NS"QMapNode<").put(keyType).put(","); - d.put(valueType).put(" >"); + d.put(valueType).put(MAP_NODE_TYPE_END); d.endItem(); d.beginItem("exp"); d.put("*('"NS"QMapNode<").put(keyType).put(","); @@ -1776,7 +1782,7 @@ static void qDumpQMap(QDumper &d) #else d.beginItem("type"); d.put(NS"QMapData::Node<").put(keyType).put(","); - d.put(valueType).put(" >"); + d.put(valueType).put(MAP_NODE_TYPE_END); d.endItem(); d.beginItem("exp"); d.put("*('"NS"QMapData::Node<").put(keyType).put(","); @@ -3036,6 +3042,27 @@ void *watchPoint(int x, int y) } #endif +// Helper to write out common expression values for CDB: +// Offsets of a map node value which looks like +// "(size_t)&(('QMapNode<QString,QString >'*)0)->value")" in gdb syntax + +template <class Key, class Value> + inline QDumper & putQMapNodeOffsetExpression(const char *keyType, + const char *valueType, + QDumper &d) +{ + QMapNode<Key, Value> *mn = 0; + const int valueOffset = (char *)&(mn->value) - (char*)mn; + d.put("(size_t)&(('"NS"QMapNode<"); + d.put(keyType); + d.put(','); + d.put(valueType); + d.put(">'*)0)->value=\""); + d.put(valueOffset); + d.put('"'); + return d; +} + extern "C" Q_DECL_EXPORT void *qDumpObjectData440( int protocolVersion, @@ -3136,8 +3163,30 @@ void *qDumpObjectData440( #endif .put("std::string=\"").put(sizeof(std::string)).put("\",") .put("std::wstring=\"").put(sizeof(std::wstring)).put("\",") - .put("std::allocator=\"").put(sizeof(std::allocator<int>)) + .put("std::allocator=\"").put(sizeof(std::allocator<int>)).put("\",") +#if QT_VERSION >= 0x040500 + .put(NS"QSharedPointer=\"").put(sizeof(QSharedPointer<int>)).put("\",") + .put(NS"QSharedDataPointer=\"").put(sizeof(QSharedDataPointer<QSharedData>)).put("\",") + .put(NS"QWeakPointer=\"").put(sizeof(QWeakPointer<int>)).put("\",") +#endif + .put("QPointer=\"").put(sizeof(QPointer<QObject>)).put("\",") + // Common map node types + .put(NS"QMapNode<int,int>=\"").put(sizeof(QMapNode<int,int >)).put("\",") + .put(NS"QMapNode<int,"NS"QString>=\"").put(sizeof(QMapNode<int, QString>)).put("\",") + .put(NS"QMapNode<int,"NS"QVariant>=\"").put(sizeof(QMapNode<int, QVariant>)).put("\",") + .put(NS"QMapNode<"NS"QString,int>=\"").put(sizeof(QMapNode<QString, int>)).put("\",") + .put(NS"QMapNode<"NS"QString,"NS"QString>=\"").put(sizeof(QMapNode<QString, QString>)).put("\",") + .put(NS"QMapNode<"NS"QString,"NS"QVariant>=\"").put(sizeof(QMapNode<QString, QVariant>)) .put("\"}"); + // Write out common expression values for CDB + d.put(",expressions={"); + putQMapNodeOffsetExpression<int,int>("int", "int", d).put(','); + putQMapNodeOffsetExpression<int,QString>("int", NS"QString", d).put(','); + putQMapNodeOffsetExpression<int,QVariant>("int", NS"QVariant", d).put(','); + putQMapNodeOffsetExpression<QString,int>(NS"QString", "int", d).put(','); + putQMapNodeOffsetExpression<QString,QString>(NS"QString", NS"QString", d).put(','); + putQMapNodeOffsetExpression<QString,QVariant>(NS"QString", NS"QVariant", d); + d.put('}'); d.disarm(); } diff --git a/share/qtcreator/gdbmacros/test/main.cpp b/share/qtcreator/gdbmacros/test/main.cpp index 3d2adbd4a2a..fe6c2c6723e 100644 --- a/share/qtcreator/gdbmacros/test/main.cpp +++ b/share/qtcreator/gdbmacros/test/main.cpp @@ -29,12 +29,15 @@ #include <QtCore/QStringList> #include <QtCore/QVector> +#include <QtCore/QSharedPointer> #include <QtCore/QTimer> +#include <QtCore/QMap> #include <string> #include <list> #include <vector> #include <set> +#include <map> #include <stdio.h> #include <string.h> @@ -102,6 +105,17 @@ static int dumpQString() return 0; } +static int dumpQSharedPointerQString() +{ + QSharedPointer<QString> test(new QString(QLatin1String("hallo"))); + prepareInBuffer("QSharedPointer", "local.sharedpointerqstring", "local.local.sharedpointerqstring", "QString"); + qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(QString), 0, 0, 0); + fputs(qDumpOutBuffer, stdout); + fputc('\n', stdout); + QString uninitialized; + return 0; +} + static int dumpQStringList() { QStringList test = QStringList() << QLatin1String("item1") << QLatin1String("item2"); @@ -132,6 +146,34 @@ static int dumpQIntVector() return 0; } +static int dumpQMapIntInt() +{ + QMap<int,int> test; + QMapNode<int,int> mapNode; + const int valueOffset = (char*)&(mapNode.value) - (char*)&mapNode; + test.insert(42, 43); + test.insert(43, 44); + prepareInBuffer("QMap", "local.qmapintint", "local.qmapintint", "int@int"); + qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(int), sizeof(mapNode), valueOffset); + fputs(qDumpOutBuffer, stdout); + fputc('\n', stdout); + return 0; +} + +static int dumpQMapIntString() +{ + QMap<int,QString> test; + QMapNode<int,QString> mapNode; + const int valueOffset = (char*)&(mapNode.value) - (char*)&mapNode; + test.insert(42, QLatin1String("fortytwo")); + test.insert(43, QLatin1String("fortytree")); + prepareInBuffer("QMap", "local.qmapintqstring", "local.qmapintqstring", "int@QString"); + qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(QString), sizeof(mapNode), valueOffset); + fputs(qDumpOutBuffer, stdout); + fputc('\n', stdout); + return 0; +} + // --------------- std types static int dumpStdString() @@ -238,6 +280,20 @@ static int dumpStdStringSet() return 0; } +static int dumpStdMapIntString() +{ + std::map<int,std::string> test; + std::map<int,std::string>::value_type entry(42, std::string("fortytwo")); + test.insert(entry); + const int valueOffset = (char*)&(entry.second) - (char*)&entry; + prepareInBuffer("std::map", "local.stdmapintstring", "local.stdmapintstring", + "int@std::basic_string<char,std::char_traits<char>,std::allocator<char> >@std::less<int>@std::allocator<std::pair<const int,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >"); + qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(std::string), valueOffset, 0); + fputs(qDumpOutBuffer, stdout); + fputc('\n', stdout); + return 0; +} + static int dumpQObject() { // Requires the childOffset to be know, but that is not critical @@ -249,6 +305,47 @@ static int dumpQObject() return 0; } +static bool dumpType(const char *arg) +{ + 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, "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; +} + int main(int argc, char *argv[]) { printf("Running query protocol\n"); @@ -266,34 +363,8 @@ int main(int argc, char *argv[]) continue; } printf("\nTesting %s\n", arg); - if (!qstrcmp(arg, "QString")) - dumpQString(); - if (!qstrcmp(arg, "QStringList")) - dumpQStringList(); - if (!qstrcmp(arg, "QList<int>")) - dumpQIntList(); - if (!qstrcmp(arg, "QList<std::string>")) - dumpStdStringQList(); - if (!qstrcmp(arg, "QVector<int>")) - dumpQIntVector(); - if (!qstrcmp(arg, "string")) - dumpStdString(); - if (!qstrcmp(arg, "wstring")) - dumpStdWString(); - if (!qstrcmp(arg, "list<int>")) - dumpStdIntList(); - if (!qstrcmp(arg, "list<string>")) - dumpStdStringList(); - if (!qstrcmp(arg, "vector<int>")) - dumpStdIntVector(); - if (!qstrcmp(arg, "vector<string>")) - dumpStdStringVector(); - if (!qstrcmp(arg, "set<int>")) - dumpStdIntSet(); - if (!qstrcmp(arg, "set<string>")) - dumpStdStringSet(); - if (!qstrcmp(arg, "QObject")) - dumpQObject(); + if (!dumpType(arg)) + printf("\nUnhandled type: %s\n", arg); } return 0; } diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp index 910eae6aa7e..0209a08e2b4 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.cpp +++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp @@ -576,8 +576,9 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool return DumpOk; // Cache types that fail due to complicated template size expressions. // Exceptions OTOH might occur when accessing variables that are not - // yet initialized in a particular breakpoint. That should be ignored - if (der == DumpExecuteSizeFailed) + // yet initialized in a particular breakpoint. That should be ignored. + // Also fail for complex expression that were not cached/replaced by the helper. + if (der == DumpExecuteSizeFailed || der == DumpComplexExpressionEncountered) m_failedTypes.push_back(wd.type); // log error *errorMessage = msgDumpFailed(wd, errorMessage); @@ -611,6 +612,12 @@ CdbDumperHelper::DumpExecuteResult if (!sizeOk) return DumpExecuteSizeFailed; ep = QString::number(size); + continue; + } + // We cannot evaluate any other expressions than 'sizeof()' ;-( + if (!ep.isEmpty() && !ep.at(0).isDigit()) { + *errorMessage = QString::fromLatin1("Unable to evaluate: '%1'").arg(ep); + return DumpComplexExpressionEncountered; } } // Execute call diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.h b/src/plugins/debugger/cdb/cdbdumperhelper.h index ccff93f0b62..3359093ab52 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.h +++ b/src/plugins/debugger/cdb/cdbdumperhelper.h @@ -109,7 +109,9 @@ private: bool callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr, bool ignoreAccessViolation, QString *errorMessage); - enum DumpExecuteResult { DumpExecuteOk, DumpExecuteSizeFailed, DumpExecuteCallFailed }; + enum DumpExecuteResult { DumpExecuteOk, DumpExecuteSizeFailed, + DumpComplexExpressionEncountered, + DumpExecuteCallFailed }; DumpExecuteResult executeDump(const WatchData &wd, const QtDumperHelper::TypeData& td, bool dumpChildren, int source, QList<WatchData> *result, QString *errorMessage); diff --git a/src/plugins/debugger/cdb/cdbstackframecontext.cpp b/src/plugins/debugger/cdb/cdbstackframecontext.cpp index 0a4cbc1280b..9cd7589636e 100644 --- a/src/plugins/debugger/cdb/cdbstackframecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstackframecontext.cpp @@ -112,7 +112,7 @@ WatchHandleDumperInserter::WatchHandleDumperInserter(WatchHandler *wh, const SharedPointerCdbDumperHelper &dumper) : m_hexNullPattern(QLatin1String("0x0+")), m_wh(wh), - m_dumper(dumper) + m_dumper(dumper) { Q_ASSERT(m_hexNullPattern.isValid()); } @@ -179,6 +179,10 @@ WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd) case CdbDumperHelper::DumpOk: 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); // Discard the original item and insert the dumper results foreach(const WatchData &dwd, m_dumperResult) m_wh->insertData(dwd); @@ -245,13 +249,20 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal, errorMessage); } - // Expand dumper items (not implemented) + // Expand artifical dumper items if (incompleteLocal.source == OwnerDumper) { - if (debugCDBWatchHandling) - qDebug() << "ignored dumper item"; - WatchData wd = incompleteLocal; - wd.setAllUnneeded(); - wh->insertData(wd); + QList<WatchData> dumperResult; + const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(incompleteLocal, true, OwnerDumper, &dumperResult, errorMessage); + if (dr == CdbDumperHelper::DumpOk) { + 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; + wd.setAllUnneeded(); + wh->insertData(wd); + } return true; } diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp index 60b75fdc761..6933b68ff8e 100644 --- a/src/plugins/debugger/watchutils.cpp +++ b/src/plugins/debugger/watchutils.cpp @@ -560,14 +560,10 @@ void QtDumperHelper::TypeData::clear() } // ----------------- QtDumperHelper -const QString stdAllocatorPrefix = QLatin1String("std::allocator"); - QtDumperHelper::QtDumperHelper() : - m_intSize(0), - m_pointerSize(0), - m_stdAllocatorSize(0), m_qtVersion(0) { + qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0); } void QtDumperHelper::clear() @@ -576,9 +572,8 @@ void QtDumperHelper::clear() m_qtVersion = 0; m_qtNamespace.clear(); m_sizeCache.clear(); - m_intSize = 0; - m_pointerSize = 0; - m_stdAllocatorSize = 0; + qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0); + m_expressionCache.clear(); } static inline void formatQtVersion(int v, QTextStream &str) @@ -593,13 +588,15 @@ QString QtDumperHelper::toString(bool debug) const QTextStream str(&rc); str << "version="; formatQtVersion(m_qtVersion, str); - str << " namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types: "; + str << " 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() << ']'; } - str << "Sizes: intsize=" << m_intSize << " pointer size=" << m_pointerSize - << " allocatorsize=" << m_stdAllocatorSize; + str << "\nSpecial size: "; + for (int i = 0; i < SpecialSizeCount; i++) + str << ' ' << m_specialSizes[i]; + str << "\nSize cache: "; const SizeCache::const_iterator scend = m_sizeCache.constEnd(); for (SizeCache::const_iterator it = m_sizeCache.constBegin(); it != scend; ++it) { str << ' ' << it.key() << '=' << it.value(); @@ -693,22 +690,23 @@ QtDumperHelper::Type QtDumperHelper::specialType(QString s) return UnknownType; } -bool QtDumperHelper::needsExpressionSyntax(Type t) +QtDumperHelper::ExpressionRequirement QtDumperHelper::expressionRequirements(Type t) { switch (t) { - case QAbstractItemType: - case QObjectSlotType: - case QObjectSignalType: - case QMapType: - case QVectorType: - case QMultiMapType: - case QMapNodeType: - case StdMapType: - return true; - default: - break; + case QAbstractItemType: + case QObjectSlotType: + case QObjectSignalType: + case QVectorType: + case StdMapType: + return NeedsComplexExpression; + case QMapType: + case QMultiMapType: + case QMapNodeType: + return NeedsCachedExpression; + default: + break; } - return false; + return NeedsNoExpression; } QString QtDumperHelper::qtVersionString() const @@ -745,7 +743,7 @@ void QtDumperHelper::parseQueryTypes(const QStringList &l, Debugger debugger) const Type t = specialType(l.at(i)); if (t != UnknownType) { // Exclude types that require expression syntax for CDB - if (debugger == GdbDebugger || !needsExpressionSyntax(t)) + if (debugger == GdbDebugger || expressionRequirements(t) != NeedsComplexExpression) m_nameTypeMap.insert(l.at(i), t); } else { m_nameTypeMap.insert(l.at(i), SupportedType); @@ -940,6 +938,7 @@ public: QString qtVersion; QStringList types; QList<SizeEntry> sizes; + QMap<QString, QString> expressionCache; }; inline Data data() const { return m_data; } @@ -952,10 +951,12 @@ protected: virtual bool handleValue(const char *k, int size); private: - enum Mode { None, ExpectingDumpers, ExpectingVersion, ExpectingNameSpace, ExpectingSizes }; + enum Mode { None, ExpectingDumpers, ExpectingVersion, + ExpectingNameSpace, ExpectingSizes, ExpectingExpressionCache }; Mode m_mode; Data m_data; QString m_lastSizeType; + QString m_lastExpression; }; QueryDumperParser::QueryDumperParser(const char *s) : @@ -965,10 +966,16 @@ QueryDumperParser::QueryDumperParser(const char *s) : } bool QueryDumperParser::handleKeyword(const char *k, int size) -{ - if (m_mode == ExpectingSizes) { +{ + switch (m_mode) { + case ExpectingSizes: m_lastSizeType = QString::fromLatin1(k, size); return true; + case ExpectingExpressionCache: + m_lastExpression = QString::fromLatin1(k, size); + return true; + default: + break; } if (!qstrncmp(k, "dumpers", size)) { m_mode = ExpectingDumpers; @@ -986,6 +993,10 @@ bool QueryDumperParser::handleKeyword(const char *k, int size) m_mode = ExpectingSizes; return true; } + if (!qstrncmp(k, "expressions", size)) { + m_mode = ExpectingExpressionCache; + return true; + } qWarning("%s Unexpected keyword %s.\n", Q_FUNC_INFO, QByteArray(k, size).constData()); return false; } @@ -1026,6 +1037,9 @@ bool QueryDumperParser::handleValue(const char *k, int size) case ExpectingSizes: m_data.sizes.push_back(SizeEntry(m_lastSizeType, QString::fromLatin1(k, size).toInt())); break; + case ExpectingExpressionCache: + m_data.expressionCache.insert(m_lastExpression, QString::fromLatin1(k, size)); + break; } return true; } @@ -1042,25 +1056,24 @@ bool QtDumperHelper::parseQuery(const char *data, Debugger debugger) parseQueryTypes(parser.data().types, debugger); foreach (const QueryDumperParser::SizeEntry &se, parser.data().sizes) addSize(se.first, se.second); + m_expressionCache = parser.data().expressionCache; + qDebug() << m_expressionCache; return true; } void QtDumperHelper::addSize(const QString &name, int size) { // Special interest cases + if (name == QLatin1String("char*")) { + m_specialSizes[PointerSize] = size; + return; + } + const SpecialSizeType st = specialSizeType(name); + if (st != SpecialSizeCount) { + m_specialSizes[st] = size; + return; + } do { - if (name == QLatin1String("char*")) { - m_pointerSize = size; - break; - } - if (name == QLatin1String("int")) { - m_intSize = size; - break; - } - if (name.startsWith(stdAllocatorPrefix)) { - m_stdAllocatorSize = size; - break; - } if (name == QLatin1String("std::string")) { m_sizeCache.insert(QLatin1String("std::basic_string<char,std::char_traits<char>,std::allocator<char>>"), size); break; @@ -1104,11 +1117,13 @@ QtDumperHelper::TypeData QtDumperHelper::typeData(const QString &typeName) const QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName, Debugger /* debugger */) const { - // Look up fixed types - if (m_pointerSize && isPointerType(typeName)) - return QString::number(m_pointerSize); - if (m_stdAllocatorSize && typeName.startsWith(stdAllocatorPrefix)) - return QString::number(m_stdAllocatorSize); + // Look up special size types + const SpecialSizeType st = specialSizeType(typeName); + if (st != SpecialSizeCount) { + if (const int size = m_specialSizes[st]) + return QString::number(size); + } + // Look up size cache const SizeCache::const_iterator sit = m_sizeCache.constFind(typeName); if (sit != m_sizeCache.constEnd()) return QString::number(sit.value()); @@ -1116,6 +1131,42 @@ QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName, return sizeofTypeExpression(typeName); } +QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QString &typeName) +{ + if (isPointerType(typeName)) + return PointerSize; + static const QString intType = QLatin1String("int"); + static const QString stdAllocatorPrefix = QLatin1String("std::allocator"); + static const QString qPointerPrefix = QLatin1String("QPointer"); + static const QString qSharedPointerPrefix = QLatin1String("QSharedPointer"); + static const QString qSharedDataPointerPrefix = QLatin1String("QSharedDataPointer"); + static const QString qWeakPointerPrefix = QLatin1String("QWeakPointer"); + if (typeName == intType) + return IntSize; + if (typeName.startsWith(stdAllocatorPrefix)) + return StdAllocatorSize; + if (typeName.startsWith(qPointerPrefix)) + return QPointerSize; + if (typeName.startsWith(qSharedPointerPrefix)) + return QSharedPointerSize; + if (typeName.startsWith(qSharedDataPointerPrefix)) + return QSharedDataPointerSize; + if (typeName.startsWith(qWeakPointerPrefix)) + return QWeakPointerSize; + return SpecialSizeCount; +} + +static inline bool isInteger(const QString &n) +{ + const int size = n.size(); + if (!size) + return false; + for (int i = 0; i < size; i++) + if (!n.at(i).isDigit()) + return false; + return true; +} + void QtDumperHelper::evaluationParameters(const WatchData &data, const TypeData &td, Debugger debugger, @@ -1265,6 +1316,20 @@ void QtDumperHelper::evaluationParameters(const WatchData &data, break; } + // Look up expressions in the cache + if (!m_expressionCache.empty()) { + const QMap<QString, QString>::const_iterator excCend = m_expressionCache.constEnd(); + const QStringList::iterator eend = extraArgs.end(); + for (QStringList::iterator it = extraArgs.begin(); it != eend; ++it) { + QString &e = *it; + if (!e.isEmpty() && e != zero && !isInteger(e)) { + const QMap<QString, QString>::const_iterator eit = m_expressionCache.constFind(e); + if (eit != excCend) + e = eit.value(); + } + } + } + inBuffer->clear(); inBuffer->append(outertype.toUtf8()); inBuffer->append('\0'); diff --git a/src/plugins/debugger/watchutils.h b/src/plugins/debugger/watchutils.h index 52339fb0bfd..d9a43d136f9 100644 --- a/src/plugins/debugger/watchutils.h +++ b/src/plugins/debugger/watchutils.h @@ -198,7 +198,16 @@ public: // 'data' excludes the leading indicator character. static bool parseValue(const char *data, QtDumperResult *r); - static bool needsExpressionSyntax(Type t); + // What kind of debugger expressions are required to dump that type. + // A debugger with restricted expression syntax can handle + // 'NeedsNoExpression' and 'NeedsCachedExpression' if it is found in + // the cache. + enum ExpressionRequirement { + NeedsNoExpression, // None, easy. + NeedsCachedExpression, // Common values might be found in expression cache. + NeedsComplexExpression // Totally arbitrary, adress-dependent expressions + }; + static ExpressionRequirement expressionRequirements(Type t); QString toString(bool debug = false) const; @@ -214,9 +223,20 @@ private: NameTypeMap m_nameTypeMap; SizeCache m_sizeCache; - int m_intSize; - int m_pointerSize; - int m_stdAllocatorSize; + + // The initial dumper query function returns sizes of some special + // types to aid CDB since it cannot determine the size of classes. + // They are not complete (std::allocator<X>). + enum SpecialSizeType { IntSize, PointerSize, StdAllocatorSize, + QSharedPointerSize, QSharedDataPointerSize, + QWeakPointerSize, QPointerSize, SpecialSizeCount }; + + // Resolve name to enumeration or SpecialSizeCount (invalid) + static SpecialSizeType specialSizeType(const QString &t); + + int m_specialSizes[SpecialSizeCount]; + + QMap<QString, QString> m_expressionCache; int m_qtVersion; QString m_qtNamespace; }; -- GitLab