diff --git a/share/qtcreator/gdbmacros/gdbmacros.cpp b/share/qtcreator/gdbmacros/gdbmacros.cpp index 5f039321de4045922534ecd47174ed04cb034abe..dcd016495a64194906bbe254d17ee3751f6e5506 100644 --- a/share/qtcreator/gdbmacros/gdbmacros.cpp +++ b/share/qtcreator/gdbmacros/gdbmacros.cpp @@ -174,6 +174,9 @@ QT_BEGIN_NAMESPACE struct Sender { QObject *sender; int signal; int ref; }; +const char *stdStringTypeC = "std::basic_string<char,std::char_traits<char>,std::allocator<char> >"; +const char *stdWideStringTypeUShortC = "std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >"; + #if QT_VERSION < 0x040600 struct Connection { @@ -905,7 +908,8 @@ static inline void dumpChildNumChildren(QDumper &d, InnerValueResult innerValueR } } -static InnerValueResult qDumpInnerValueHelper(QDumper &d, const char *type, const void *addr, +// Called by templates, so, not static. +InnerValueResult qDumpInnerValueHelper(QDumper &d, const char *type, const void *addr, const char *field = "value") { char buf[30]; @@ -1017,13 +1021,13 @@ static InnerValueResult qDumpInnerValueHelper(QDumper &d, const char *type, cons return InnerValueNotHandled; case 't': if (isEqual(type, "std::string") - || isEqual(type, "std::basic_string<char,std::char_traits<char>,std::allocator<char> >")) { + || isEqual(type, stdStringTypeC)) { d.putCommaIfNeeded(); dumpStdStringValue(d, *reinterpret_cast<const std::string*>(addr)); return InnerValueNoFurtherChildren; } if (isEqual(type, "std::wstring") - || isEqual(type, "std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >")) { + || isEqual(type, stdWideStringTypeUShortC)) { dumpStdWStringValue(d, *reinterpret_cast<const std::wstring*>(addr)); return InnerValueNoFurtherChildren; } @@ -2905,9 +2909,16 @@ static void qDumpStdList(QDumper &d) d.disarm(); } -static void qDumpStdMap(QDumper &d) +/* Dump out an arbitrary map. To iterate the map, + * it is cast to a map of <KeyType,Value>. 'int' can be used for both + * for all types if the implementation does not depend on the types + * which is the case for GNU STL. The implementation used by MS VC, however, + * does depend on the key/value type, so, special cases need to be hardcoded. */ + +template <class KeyType, class ValueType> +static void qDumpStdMapHelper(QDumper &d) { - typedef std::map<int, int> DummyType; + typedef std::map<KeyType, ValueType> DummyType; const DummyType &map = *reinterpret_cast<const DummyType*>(d.data); const char *keyType = d.templateParameters[0]; const char *valueType = d.templateParameters[1]; @@ -2915,14 +2926,15 @@ static void qDumpStdMap(QDumper &d) qCheckAccess(p); p = deref(p); - int nn = map.size(); + const int nn = map.size(); if (nn < 0) return; - DummyType::const_iterator it = map.begin(); - for (int i = 0; i < nn && i < 10 && it != map.end(); ++i, ++it) + Q_TYPENAME DummyType::const_iterator it = map.begin(); + const Q_TYPENAME DummyType::const_iterator cend = map.end(); + for (int i = 0; i < nn && i < 10 && it != cend; ++i, ++it) qCheckAccess(it.operator->()); - QByteArray strippedInnerType = stripPointerType(d.innertype); + const QByteArray strippedInnerType = stripPointerType(d.innertype); d.putItem("numchild", nn); d.putItemCount("value", nn); d.putItem("valuedisabled", "true"); @@ -2937,6 +2949,7 @@ static void qDumpStdMap(QDumper &d) pairType[strlen(pairType) - 2] = 0; d.putItem("pairtype", pairType); + InnerValueResult innerValueResult = InnerValueChildrenSpecified; if (d.dumpChildren) { bool isSimpleKey = isSimpleType(keyType); bool isSimpleValue = isSimpleType(valueType); @@ -2951,12 +2964,12 @@ static void qDumpStdMap(QDumper &d) d.beginChildren(); it = map.begin(); - for (int i = 0; i < 1000 && it != map.end(); ++i, ++it) { + for (int i = 0; i < 1000 && it != cend; ++i, ++it) { d.beginHash(); const void *node = it.operator->(); d.putItem("name", i); qDumpInnerValueHelper(d, keyType, node, "key"); - qDumpInnerValueHelper(d, valueType, addOffset(node, valueOffset)); + innerValueResult = qDumpInnerValueHelper(d, valueType, addOffset(node, valueOffset)); if (isSimpleKey && isSimpleValue) { d.putItem("type", valueType); d.putItem("addr", addOffset(node, valueOffset)); @@ -2972,22 +2985,61 @@ static void qDumpStdMap(QDumper &d) d.putEllipsis(); d.endChildren(); } + dumpChildNumChildren(d, innerValueResult); d.disarm(); } -static void qDumpStdSet(QDumper &d) +static void qDumpStdMap(QDumper &d) +{ +#ifdef Q_CC_MSVC + // As the map implementation inherits from a base class + // depending on the key, use something equivalent to iterate it. + const int keySize = d.extraInt[0]; + const int valueSize = d.extraInt[1]; + if (keySize == valueSize) { + if (keySize == sizeof(int)) { + qDumpStdMapHelper<int,int>(d); + return; + } + if (keySize == sizeof(std::string)) { + qDumpStdMapHelper<std::string,std::string>(d); + return; + } + return; + } + if (keySize == sizeof(int) && valueSize == sizeof(std::string)) { + qDumpStdMapHelper<int,std::string>(d); + return; + } + if (keySize == sizeof(std::string) && valueSize == sizeof(int)) { + qDumpStdMapHelper<std::string,int>(d); + return; + } +#else + qDumpStdMapHelper<int,int>(d); +#endif +} + +/* Dump out an arbitrary set. To iterate the set, + * it is cast to a set of <KeyType>. 'int' can be used + * for all types if the implementation does not depend on the key type + * which is the case for GNU STL. The implementation used by MS VC, however, + * does depend on the key type, so, special cases need to be hardcoded. */ + +template <class KeyType> +static void qDumpStdSetHelper(QDumper &d) { - typedef std::set<int> DummyType; + typedef std::set<KeyType> DummyType; const DummyType &set = *reinterpret_cast<const DummyType*>(d.data); const void *p = d.data; qCheckAccess(p); p = deref(p); - int nn = set.size(); + const int nn = set.size(); if (nn < 0) return; - DummyType::const_iterator it = set.begin(); - const DummyType::const_iterator cend = set.end(); + Q_TYPENAME DummyType::const_iterator it = set.begin(); + const Q_TYPENAME DummyType::const_iterator cend = set.end(); for (int i = 0; i < nn && i < 10 && it != cend; ++i, ++it) qCheckAccess(it.operator->()); @@ -3024,6 +3076,29 @@ static void qDumpStdSet(QDumper &d) d.disarm(); } +static void qDumpStdSet(QDumper &d) +{ +#ifdef Q_CC_MSVC + // As the set implementation inherits from a base class + // depending on the key, use something equivalent to iterate it. + const int innerSize = d.extraInt[0]; + if (innerSize == sizeof(int)) { + qDumpStdSetHelper<int>(d); + return; + } + if (innerSize == sizeof(std::string)) { + qDumpStdSetHelper<std::string>(d); + return; + } + if (innerSize == sizeof(std::wstring)) { + qDumpStdSetHelper<std::wstring>(d); + return; + } +#else + qDumpStdSetHelper<int>(d); +#endif +} + static void qDumpStdString(QDumper &d) { const std::string &str = *reinterpret_cast<const std::string *>(d.data); @@ -3329,12 +3404,37 @@ template <class Key, class Value> d.put(keyType); d.put(','); d.put(valueType); + if (valueType[qstrlen(valueType) - 1] == '>') + d.put(' '); d.put(">'*)0)->value=\""); d.put(valueOffset); d.put('"'); return d; } +// Helper to write out common expression values for CDB: +// Offsets of a std::pair for dumping std::map node value which look like +// "(size_t)&(('std::pair<int const ,unsigned int>'*)0)->second" + +template <class Key, class Value> + inline QDumper & putStdPairValueOffsetExpression(const char *keyType, + const char *valueType, + QDumper &d) +{ + std::pair<Key, Value> *p = 0; + const int valueOffset = (char *)&(p->second) - (char*)p; + d.put("(size_t)&(('std::pair<"); + d.put(keyType); + d.put(" const ,"); + d.put(valueType); + if (valueType[qstrlen(valueType) - 1] == '>') + d.put(' '); + d.put(">'*)0)->second=\""); + d.put(valueOffset); + d.put('"'); + return d; +} + extern "C" Q_DECL_EXPORT void *qDumpObjectData440( int protocolVersion, @@ -3461,7 +3561,18 @@ void *qDumpObjectData440( 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); + putQMapNodeOffsetExpression<QString,QVariant>(NS"QString", NS"QVariant", d).put(','); + // Std Pairs + putStdPairValueOffsetExpression<int,int>("int","int", d).put(','); + putStdPairValueOffsetExpression<QString,QString>(NS"QString",NS"QString", d).put(','); + putStdPairValueOffsetExpression<int,QString>("int",NS"QString", d).put(','); + putStdPairValueOffsetExpression<QString,int>(NS"QString", "int", d).put(','); + putStdPairValueOffsetExpression<std::string,std::string>(stdStringTypeC, stdStringTypeC, d).put(','); + putStdPairValueOffsetExpression<int,std::string>("int", stdStringTypeC, d).put(','); + putStdPairValueOffsetExpression<std::string,int>(stdStringTypeC, "int", d.put(',')); + putStdPairValueOffsetExpression<std::wstring,std::wstring>(stdWideStringTypeUShortC, stdWideStringTypeUShortC, d).put(','); + putStdPairValueOffsetExpression<int,std::wstring>("int", stdWideStringTypeUShortC, d).put(','); + putStdPairValueOffsetExpression<std::wstring,int>(stdWideStringTypeUShortC, "int", d); d.put('}'); d.disarm(); } diff --git a/share/qtcreator/gdbmacros/test/main.cpp b/share/qtcreator/gdbmacros/test/main.cpp index 0ec31578b0e6b82d03fcb6758807d270ee9bb005..8b24d55aca60fbfe7c081c8ea84f9f3e84a2d6b8 100644 --- a/share/qtcreator/gdbmacros/test/main.cpp +++ b/share/qtcreator/gdbmacros/test/main.cpp @@ -308,6 +308,18 @@ static int dumpStdStringSet() return 0; } +static int dumpStdQStringSet() +{ + std::set<QString> test; + test.insert(QLatin1String("item1")); + test.insert(QLatin1String("item2")); + prepareInBuffer("std::set", "local.stringset", "local.stringset", "QString"); + qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(QString), sizeof(std::list<int>::allocator_type), 0, 0); + fputs(qDumpOutBuffer, stdout); + fputc('\n', stdout); + return 0; +} + static int dumpStdMapIntString() { std::map<int,std::string> test; @@ -322,6 +334,22 @@ static int dumpStdMapIntString() return 0; } +static int dumpStdMapStringString() +{ + typedef std::map<std::string,std::string> TestType; + TestType test; + const TestType::value_type entry("K", "V"); + test.insert(entry); + const int valueOffset = (char*)&(entry.second) - (char*)&entry; + prepareInBuffer("std::map", "local.stdmapstringstring", "local.stdmapstringstring", + "std::basic_string<char,std::char_traits<char>,std::allocator<char> >@std::basic_string<char,std::char_traits<char>,std::allocator<char> >@std::less<int>@std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >"); + qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(std::string), 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 @@ -398,7 +426,9 @@ static TypeDumpFunctionMap registerTypes() rc.insert("vector<wstring>", dumpStdWStringVector); rc.insert("set<int>", dumpStdIntSet); rc.insert("set<string>", dumpStdStringSet); + rc.insert("set<QString>", dumpStdQStringSet); rc.insert("map<int,string>", dumpStdMapIntString); + rc.insert("map<string,string>", dumpStdMapStringString); rc.insert("QObject", dumpQObject); rc.insert("QObjectList", dumpQObjectList); rc.insert("QVariant", dumpQVariant); diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp index b4cd0bff6c782506b423aa37266c6638da2ff6a5..a42f88900d4208135bc4e60c4a7da81da2634c0f 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.cpp +++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp @@ -100,7 +100,7 @@ static bool allocDebuggeeMemory(CdbComInterfaces *cif, OutputRedirector redir(cif->debugClient, &stringHandler); if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, allocCmd, errorMessage)) return false; - // "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized + // "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized bool ok = false; const QString output = stringHandler.result(); const int lastBlank = output.lastIndexOf(QLatin1Char(' ')); @@ -176,7 +176,7 @@ static bool debuggeeLoadLibrary(IDebuggerManagerAccessForEngines *access, return false; // This will hit a breakpoint. if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, QString(QLatin1Char('g')), errorMessage)) - return false; + return false; const HRESULT hr = cif->debugControl->WaitForEvent(0, waitTimeOutMS); if (FAILED(hr)) { *errorMessage = msgComFailed("WaitForEvent", hr); @@ -280,7 +280,7 @@ void CdbDumperHelper::moduleLoadHook(const QString &module, HANDLE debuggeeHandl if (m_tryInjectLoad && module.contains(QLatin1String("Qt"), Qt::CaseInsensitive)) { // Also shows up in the log window. m_manager->showStatusMessage(msgLoading(m_library, true), 10000); - QString errorMessage; + QString errorMessage; SharedLibraryInjector sh(GetProcessId(debuggeeHandle)); if (sh.remoteInject(m_library, false, &errorMessage)) { m_state = InjectLoading; @@ -408,7 +408,7 @@ static inline bool getSymbolAddress(CIDebugSymbols *sg, } bool CdbDumperHelper::initResolveSymbols(QString *errorMessage) -{ +{ // Resolve the symbols we need (potentially namespaced). // There is a 'qDumpInBuffer' in QtCore as well. m_dumpObjectSymbol = QLatin1String("*qDumpObjectData440"); @@ -437,6 +437,7 @@ bool CdbDumperHelper::initResolveSymbols(QString *errorMessage) // Call query protocol to retrieve known types and sizes bool CdbDumperHelper::initKnownTypes(QString *errorMessage) { + const double dumperVersionRequired = 1.3; QByteArray output; QString callCmd; QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol << "(1,0,0,0,0,0,0,0)"; @@ -447,6 +448,10 @@ bool CdbDumperHelper::initKnownTypes(QString *errorMessage) if (!m_helper.parseQuery(outData, QtDumperHelper::CdbDebugger)) { *errorMessage = QString::fromLatin1("Unable to parse the dumper output: '%1'").arg(QString::fromAscii(output)); } + if (m_helper.dumperVersion() < dumperVersionRequired) { + *errorMessage = QtDumperHelper::msgDumperOutdated(dumperVersionRequired, m_helper.dumperVersion()); + return false; + } if (loadDebug) qDebug() << Q_FUNC_INFO << m_helper.toString(true); return true; @@ -521,7 +526,7 @@ bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuf return false; } // see QDumper implementation - const char result = m_buffer[0]; + const char result = m_buffer[0]; switch (result) { case 't': break; @@ -574,7 +579,7 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool } // Known type? - const QtDumperHelper::TypeData td = m_helper.typeData(wd.type); + const QtDumperHelper::TypeData td = m_helper.typeData(wd.type); if (loadDebug) qDebug() << "dumpType" << wd.type << td; if (td.type == QtDumperHelper::UnknownType) { @@ -596,7 +601,7 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool // 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); + m_failedTypes.push_back(wd.type); // log error *errorMessage = msgDumpFailed(wd, errorMessage); m_access->showDebuggerOutput(LogWarning, *errorMessage); @@ -609,7 +614,7 @@ CdbDumperHelper::DumpExecuteResult QList<WatchData> *result, QString *errorMessage) { QByteArray inBuffer; - QStringList extraParameters; + QStringList extraParameters; // Build parameter list. m_helper.evaluationParameters(wd, td, QtDumperHelper::CdbDebugger, &inBuffer, &extraParameters); // If the parameter list contains sizeof-expressions, execute them separately diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp index 098b30c86c580c6ce7baf7a6ee7066ec45bedd98..5d82835f53dad00e411b14623ca7fd350413df5d 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp @@ -86,10 +86,12 @@ static inline QString getSymbolString(IDebugSymbolGroup2 *sg, WideStringRetrievalFunction wsf, unsigned long index) { - static WCHAR nameBuffer[MAX_PATH + 1]; + // Template type names can get quite long.... + enum { BufSize = 1024 }; + static WCHAR nameBuffer[BufSize + 1]; // Name ULONG nameLength; - const HRESULT hr = (sg->*wsf)(index, nameBuffer, MAX_PATH, &nameLength); + const HRESULT hr = (sg->*wsf)(index, nameBuffer, BufSize, &nameLength); if (SUCCEEDED(hr)) { nameBuffer[nameLength] = 0; return QString::fromUtf16(reinterpret_cast<const ushort *>(nameBuffer)); diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 597b12ebd1935455e1d95f343a3e2a5835e7a454..a714e85936767a63841d0ca8f4919b3bc3aa7b35 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -246,7 +246,7 @@ void GdbEngine::initializeVariables() m_address.clear(); m_currentFunctionArgs.clear(); m_currentFrame.clear(); - m_dumperHelper = QtDumperHelper(); + m_dumperHelper.clear(); // FIXME: unhandled: //m_outputCodecState = QTextCodec::ConverterState(); @@ -3144,8 +3144,33 @@ void GdbEngine::rebuildModel() showToolTip(); } +static inline double getDumperVersion(const GdbMi &contents) +{ + const GdbMi dumperVersionG = contents.findChild("dumperversion"); + if (dumperVersionG.type() != GdbMi::Invalid) { + bool ok; + const double v = QString::fromAscii(dumperVersionG.data()).toDouble(&ok); + if (ok) + return v; + } + return 1.0; +} + +static void parseSizeCache(const GdbMi &contents, QtDumperHelper *dumperHelper) +{ + const GdbMi sizesList = contents.findChild("sizes"); + if (sizesList.type() == GdbMi::Invalid) + return; + foreach(const GdbMi &c, sizesList.children()) { + const QString name = QString::fromAscii(c.name()); + if (const int size = QString::fromAscii(c.data()).toInt()) + dumperHelper->addSize(name, size); + } +} + void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record, const QVariant &) { + const double dumperVersionRequired = 1.0; m_dumperHelper.clear(); //qDebug() << "DATA DUMPER TRIAL:" << record.toString(); @@ -3163,7 +3188,6 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record, const //qDebug() << "FOUND QT VERSION:" << qtversion.toString() << m_qtVersion; } m_dumperHelper.setQtVersion(qtv); - //qDebug() << "CONTENTS:" << contents.toString(); //qDebug() << "SIMPLE DUMPERS:" << simple.toString(); @@ -3176,17 +3200,19 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record, const if (!m_dumperInjectionLoad) // Retry if thread has not terminated yet. m_debuggingHelperState = DebuggingHelperUnavailable; q->showStatusMessage(tr("Debugging helpers not found.")); - //QMessageBox::warning(q->mainWindow(), - // tr("Cannot find special data dumpers"), - // tr("The debugged binary does not contain information needed for " - // "nice display of Qt data types.\n\n" - // "You might want to try including the file\n\n" - // ".../share/qtcreator/gdbmacros/gdbmacros.cpp\n\n" - // "into your project directly.") - // ); } else { + // Get version and sizes from dumpers. Expression cache + // currently causes errors. + const double dumperVersion = getDumperVersion(contents); + if (dumperVersion < dumperVersionRequired) { + qq->showQtDumperLibraryWarning(QtDumperHelper::msgDumperOutdated(dumperVersionRequired, dumperVersion)); + m_debuggingHelperState = DebuggingHelperUnavailable; + return; + } + parseSizeCache(contents, &m_dumperHelper); m_debuggingHelperState = DebuggingHelperAvailable; - q->showStatusMessage(tr("%n custom dumpers found.", 0, m_dumperHelper.typeCount())); + const QString successMsg = tr("Dumper version %1, %n custom dumpers found.", 0, m_dumperHelper.typeCount()).arg(dumperVersion); + q->showStatusMessage(successMsg); } //qDebug() << m_dumperHelper.toString(true); //qDebug() << m_availableSimpleDebuggingHelpers << "DATA DUMPERS AVAILABLE"; diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp index 555b6ac6cd4aabae1633b7f49fb21f68b0c0c10e..b82d54e75e1620a5743e3c2684d27ab5aaf24629 100644 --- a/src/plugins/debugger/watchutils.cpp +++ b/src/plugins/debugger/watchutils.cpp @@ -638,20 +638,30 @@ void QtDumperHelper::TypeData::clear() // ----------------- QtDumperHelper QtDumperHelper::QtDumperHelper() : - m_qtVersion(0) + m_qtVersion(0), + m_dumperVersion(1.0) { qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0); + setQClassPrefixes(QString()); } void QtDumperHelper::clear() { m_nameTypeMap.clear(); m_qtVersion = 0; + m_dumperVersion = 1.0; m_qtNamespace.clear(); m_sizeCache.clear(); qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0); m_expressionCache.clear(); - m_dumperVersion.clear(); + setQClassPrefixes(QString()); +} + +QString QtDumperHelper::msgDumperOutdated(double requiredVersion, double currentVersion) +{ + return QCoreApplication::translate("QtDumperHelper", + "Found a too-old version of the debugging helper library (%1); version %2 is required."). + arg(currentVersion).arg(requiredVersion); } static inline void formatQtVersion(int v, QTextStream &str) @@ -685,7 +695,7 @@ QString QtDumperHelper::toString(bool debug) const return QCoreApplication::translate("QtDumperHelper", "%n known types, Qt version: %1, Qt namespace: %2 Dumper version: %3", 0, QCoreApplication::CodecForTr, - m_nameTypeMap.size()).arg(qtVersionString(), nameSpace, m_dumperVersion); + m_nameTypeMap.size()).arg(qtVersionString(), nameSpace).arg(m_dumperVersion); } QtDumperHelper::Type QtDumperHelper::simpleType(const QString &simpleType) const @@ -774,11 +784,11 @@ QtDumperHelper::ExpressionRequirement QtDumperHelper::expressionRequirements(Typ switch (t) { case QAbstractItemType: case QVectorType: - case StdMapType: return NeedsComplexExpression; case QMapType: case QMultiMapType: case QMapNodeType: + case StdMapType: return NeedsCachedExpression; default: // QObjectSlotType, QObjectSignalType need the signal number, which is numeric @@ -1130,6 +1140,25 @@ bool QueryDumperParser::handleValue(const char *k, int size) return true; } +static inline QString qClassName(const QString &qtNamespace, const char *className) +{ + if (qtNamespace.isEmpty()) + return QString::fromAscii(className); + QString rc = qtNamespace; + rc += QLatin1String("::"); + rc += QString::fromAscii(className); + return rc; +} + +void QtDumperHelper::setQClassPrefixes(const QString &qNamespace) +{ + // Prefixes with namespaces + m_qPointerPrefix = qClassName(qNamespace, "QPointer"); + m_qSharedPointerPrefix = qClassName(qNamespace, "QSharedPointer"); + m_qSharedDataPointerPrefix = qClassName(qNamespace, "QSharedDataPointer"); + m_qWeakPointerPrefix = qClassName(qNamespace, "QWeakPointer"); +} + // parse a query bool QtDumperHelper::parseQuery(const char *data, Debugger debugger) { @@ -1139,14 +1168,27 @@ bool QtDumperHelper::parseQuery(const char *data, Debugger debugger) clear(); m_qtNamespace = parser.data().qtNameSpace; setQtVersion(parser.data().qtVersion); + setQClassPrefixes(m_qtNamespace); parseQueryTypes(parser.data().types, debugger); foreach (const QueryDumperParser::SizeEntry &se, parser.data().sizes) addSize(se.first, se.second); m_expressionCache = parser.data().expressionCache; - m_dumperVersion = parser.data().dumperVersion; + // Version + if (!parser.data().dumperVersion.isEmpty()) { + double dumperVersion; + bool ok; + dumperVersion = parser.data().dumperVersion.toDouble(&ok); + if (ok) + m_dumperVersion = dumperVersion; + } return true; } +void QtDumperHelper::addExpression(const QString &expression, const QString &value) +{ + m_expressionCache.insert(expression, value); +} + void QtDumperHelper::addSize(const QString &name, int size) { // Special interest cases @@ -1219,27 +1261,23 @@ QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName, return sizeofTypeExpression(typeName); } -QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QString &typeName) +QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QString &typeName) const { 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)) + if (typeName.startsWith(m_qPointerPrefix)) return QPointerSize; - if (typeName.startsWith(qSharedPointerPrefix)) + if (typeName.startsWith(m_qSharedPointerPrefix)) return QSharedPointerSize; - if (typeName.startsWith(qSharedDataPointerPrefix)) + if (typeName.startsWith(m_qSharedDataPointerPrefix)) return QSharedDataPointerSize; - if (typeName.startsWith(qWeakPointerPrefix)) + if (typeName.startsWith(m_qWeakPointerPrefix)) return QWeakPointerSize; return SpecialSizeCount; } @@ -1374,7 +1412,10 @@ void QtDumperHelper::evaluationParameters(const WatchData &data, int bracketPos = pairType.indexOf(QLatin1Char('<')); if (bracketPos != -1) pairType.remove(0, bracketPos + 1); - bracketPos = pairType.indexOf(QLatin1Char('>')); + const QChar closingBracket = QLatin1Char('>'); + bracketPos = pairType.lastIndexOf(closingBracket); + if (bracketPos != -1) + bracketPos = pairType.lastIndexOf(closingBracket, bracketPos - pairType.size() - 1); if (bracketPos != -1) pairType.truncate(bracketPos + 1); extraArgs[2] = QLatin1String("(size_t)&(('"); diff --git a/src/plugins/debugger/watchutils.h b/src/plugins/debugger/watchutils.h index d1d9b05215f9d407a5f68d9e4c01e3200d7777ae..9d02ad75f89015f04e9a82d2764fce3a14d4b928 100644 --- a/src/plugins/debugger/watchutils.h +++ b/src/plugins/debugger/watchutils.h @@ -91,7 +91,7 @@ struct QtDumperResult Child(); int keyEncoded; - int valueEncoded; + int valueEncoded; int childCount; bool valuedisabled; QString name; @@ -174,6 +174,9 @@ public: QtDumperHelper(); void clear(); + double dumperVersion() const { return m_dumperVersion; } + void setDumperVersion(double v) { m_dumperVersion = v; } + int typeCount() const; // Look up a simple, non-template type Type simpleType(const QString &simpleType) const; @@ -219,9 +222,13 @@ public: QString toString(bool debug = false) const; + // Helpers for debuggers that use a different dumper parser. void addSize(const QString &name, int size); + void addExpression(const QString &expression, const QString &value); + + static QString msgDumperOutdated(double requiredVersion, double currentVersion); -private: +private: typedef QMap<QString, Type> NameTypeMap; typedef QMap<QString, int> SizeCache; @@ -240,14 +247,21 @@ private: QWeakPointerSize, QPointerSize, SpecialSizeCount }; // Resolve name to enumeration or SpecialSizeCount (invalid) - static SpecialSizeType specialSizeType(const QString &t); + SpecialSizeType specialSizeType(const QString &t) const; int m_specialSizes[SpecialSizeCount]; QMap<QString, QString> m_expressionCache; int m_qtVersion; - QString m_dumperVersion; + double m_dumperVersion; QString m_qtNamespace; + + void setQClassPrefixes(const QString &qNamespace); + + QString m_qPointerPrefix; + QString m_qSharedPointerPrefix; + QString m_qSharedDataPointerPrefix; + QString m_qWeakPointerPrefix; }; QDebug operator<<(QDebug in, const QtDumperHelper::TypeData &d);