diff --git a/src/libs/qtcreatorcdbext/knowntype.h b/src/libs/qtcreatorcdbext/knowntype.h index feb739a87c1..b62d9ef7d32 100644 --- a/src/libs/qtcreatorcdbext/knowntype.h +++ b/src/libs/qtcreatorcdbext/knowntype.h @@ -138,6 +138,9 @@ enum KnownType KT_QPatternist_AccelTree_BasicNodeData = KT_Qt_Type + KT_Qt_MovableType + 88, KT_QFile = KT_Qt_Type + KT_HasSimpleDumper + 89, KT_QDir = KT_Qt_Type + KT_HasSimpleDumper + 90, + KT_QScriptValue = KT_Qt_Type + KT_HasSimpleDumper + 91, + KT_QHostAddress = KT_Qt_Type + KT_HasSimpleDumper + 92, + KT_QProcess = KT_Qt_Type + KT_HasSimpleDumper + 93, // Types: Qt primitive types KT_QFixed = KT_Qt_Type + KT_Qt_PrimitiveType + 90, KT_QTextItem = KT_Qt_Type + KT_Qt_PrimitiveType + 91, diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp index 54f920474cf..d2ecbe14604 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp @@ -218,16 +218,41 @@ ULONG64 SymbolGroupValue::pointerValue(ULONG64 defaultValue) const return defaultValue; } +// Read a POD value from debuggee memory and convert into host variable type POD. +// For unsigned integer types, it is possible to read a smaller debuggee-unsigned +// into a big ULONG64 on the host side (due to endianness). +template + POD readPODFromMemory(CIDebugDataSpaces *ds, ULONG64 address, ULONG debuggeeTypeSize, + POD defaultValue, std::string *errorMessage /* = 0 */) +{ + POD rc = defaultValue; + if (debuggeeTypeSize == 0 || debuggeeTypeSize > sizeof(POD)) // Safety check. + return rc; + if (const unsigned char *buffer = SymbolGroupValue::readMemory(ds, address, debuggeeTypeSize, errorMessage)) { + memcpy(&rc, buffer, debuggeeTypeSize); + delete [] buffer; + } + return rc; +} + ULONG64 SymbolGroupValue::readPointerValue(CIDebugDataSpaces *ds, ULONG64 address, std::string *errorMessage /* = 0 */) { - ULONG64 v = 0; - if (const unsigned ps = SymbolGroupValue::pointerSize()) - if (const unsigned char *buffer = SymbolGroupValue::readMemory(ds, address, ps, errorMessage)) { - memcpy(&v, buffer, ps); - delete [] buffer; - } - return v; + return readPODFromMemory(ds, address, SymbolGroupValue::pointerSize(), 0, errorMessage); +} + +ULONG64 SymbolGroupValue::readUnsignedValue(CIDebugDataSpaces *ds, + ULONG64 address, ULONG debuggeeTypeSize, + ULONG64 defaultValue, + std::string *errorMessage /* = 0 */) +{ + return readPODFromMemory(ds, address, debuggeeTypeSize, defaultValue, errorMessage); +} + +double SymbolGroupValue::readDouble(CIDebugDataSpaces *ds, ULONG64 address, double defaultValue, + std::string *errorMessage /* = 0 */) +{ + return readPODFromMemory(ds, address, sizeof(double), defaultValue, errorMessage); } // Read memory and return allocated array @@ -545,6 +570,8 @@ const QtInfo &QtInfo::get(const SymbolGroupValueContext &ctx) { static const char qtCoreDefaultModule[] = "QtCored4"; static const char qtGuiDefaultModule[] = "QtGuid4"; + static const char qtNetworkDefaultModule[] = "QtNetworkd4"; + static const char qtScriptDefaultModule[] = "QtScriptd4"; static QtInfo rc; if (!rc.coreModule.empty()) return rc; @@ -553,21 +580,23 @@ const QtInfo &QtInfo::get(const SymbolGroupValueContext &ctx) // Lookup qstrdup() to hopefully get module (potential libinfix) and namespace // Typically, this resolves to 'QtGuid4!qstrdup' and 'QtCored4!qstrdup'... const std::string qualifiedSymbol = resolveQtSymbol("qstrdup", qtCoreDefaultModule, "Core", ctx); - if (qualifiedSymbol.empty()) { + const std::string::size_type exclPos = qualifiedSymbol.find('!'); // Resolved: 'QtCored4!qstrdup' + if (exclPos == std::string::npos) { rc.coreModule = qtCoreDefaultModule; rc.guiModule = qtGuiDefaultModule; + rc.networkModule = qtNetworkDefaultModule; + rc.scriptModule = qtScriptDefaultModule; break; } // Should be 'QtCored4!qstrdup' - const std::string::size_type exclPos = qualifiedSymbol.find('!'); - if (exclPos == std::string::npos) { - rc.coreModule = qtCoreDefaultModule; - break; - } rc.coreModule = qualifiedSymbol.substr(0, exclPos); // Derive other module names 'QtXXd4' rc.guiModule = rc.coreModule; rc.guiModule.replace(0, 6, "QtGui"); + rc.networkModule = rc.coreModule; + rc.networkModule.replace(0, 6, "QtNetwork"); + rc.scriptModule = rc.coreModule; + rc.scriptModule.replace(0, 6, "QtScript"); // Any namespace? 'QtCored4!nsp::qstrdup' const std::string::size_type nameSpaceStart = exclPos + 1; const std::string::size_type colonPos = qualifiedSymbol.find(':', nameSpaceStart); @@ -1070,6 +1099,8 @@ static KnownType knownClassTypeHelper(const std::string &type, return KT_QXmltem; if (!type.compare(qPos, 8, "QXmlName")) return KT_QXmlName; + if (!type.compare(qPos, 8, "QProcess")) + return KT_QProcess; break; case 9: if (!type.compare(qPos, 9, "QBitArray")) @@ -1124,6 +1155,10 @@ static KnownType knownClassTypeHelper(const std::string &type, case 12: if (!type.compare(qPos, 12, "QKeySequence")) return KT_QKeySequence; + if (!type.compare(qPos, 12, "QHostAddress")) + return KT_QHostAddress; + if (!type.compare(qPos, 12, "QScriptValue")) + return KT_QScriptValue; break; case 13: if (!type.compare(qPos, 13, "QTextFragment")) @@ -1385,8 +1420,10 @@ static ULONG64 addressOfQPrivateMember(const SymbolGroupValue &v, QPrivateDumpMo unsigned additionalOffset = 0) { std::string errorMessage; - // Dererence d-Ptr. - ULONG64 dAddress = v.address(); + // Dererence d-Ptr (Pointer/Value duality: Value or object address). + ULONG64 dAddress = SymbolGroupValue::isPointerType(v.type()) ? v.pointerValue() : v.address(); + if (!dAddress) + return 0; if (mode == QPDM_qVirtual) // Skip vtable. dAddress += SymbolGroupValue::pointerSize(); const ULONG64 dptr = SymbolGroupValue::readPointerValue(v.context().dataspaces, @@ -1412,6 +1449,11 @@ static bool dumpQStringFromQPrivateClass(const SymbolGroupValue &v, return false; const std::string dumpType = QtInfo::get(v.context()).prependQtCoreModule("QString"); const std::string symbolName = SymbolGroupValue::pointedToSymbolName(stringAddress , dumpType); + if (SymbolGroupValue::verbose) + DebugPrint() << "dumpQStringFromQPrivateClass of " << v.name() << '/' + << v.type() << " mode=" << mode + << " offset=" << additionalOffset << " address=0x" << std::hex << stringAddress + << std::dec << " expr=" << symbolName; SymbolGroupNode *stringNode = v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage); if (!stringNode) @@ -1474,6 +1516,63 @@ static inline bool dumpQFile(const SymbolGroupValue &v, std::wostream &str) return dumpQStringFromQPrivateClass(v, QPDM_qVirtual, qIoDevicePrivateSize, str); } +/* Dump QHostAddress, for whose private class no debugging information is available. + * Dump string 'ipString' past of its private class. Does not currently work? */ +static inline bool dumpQHostAddress(const SymbolGroupValue &v, std::wostream &str) +{ + // Determine offset in private struct: qIPv6AddressType (array, unaligned) + uint32 + enum. + static unsigned qIPv6AddressSize = 0; + if (!qIPv6AddressSize) { + const std::string qIPv6AddressType = QtInfo::get(v.context()).prependQtNetworkModule("QIPv6Address"); + qIPv6AddressSize = SymbolGroupValue::sizeOf(qIPv6AddressType.c_str()); + } + if (!qIPv6AddressSize) + return false; + const unsigned offset = padOffset(8 + qIPv6AddressSize); + return dumpQStringFromQPrivateClass(v, QPDM_None, offset, str); +} + +/* Dump QProcess, for whose private class no debugging information is available. + * Dump string 'program' string with empirical offset. */ +static inline bool dumpQProcess(const SymbolGroupValue &v, std::wostream &str) +{ + const unsigned offset = SymbolGroupValue::pointerSize() == 8 ? 424 : 260; + return dumpQStringFromQPrivateClass(v, QPDM_qVirtual, offset, str); +} + +/* Dump QScriptValue, for whose private class no debugging information is available. + * Private class has a pointer to engine, type enumeration and a JSC:JValue and double/QString + * for respective types. */ +static inline bool dumpQScriptValue(const SymbolGroupValue &v, std::wostream &str) +{ + std::string errorMessage; + // Read out type + const ULONG64 privateAddress = addressOfQPrivateMember(v, QPDM_None, 0); + if (!privateAddress) { // Can actually be 0 for default-constructed + str << L""; + return true; + } + const unsigned ps = SymbolGroupValue::pointerSize(); + // Offsets of QScriptValuePrivate + const unsigned jscScriptValueSize = 8; // Union of double and rest. + const unsigned doubleValueOffset = 2 * ps + jscScriptValueSize; + const unsigned stringValueOffset = doubleValueOffset + sizeof(double); + const ULONG64 type = + SymbolGroupValue::readUnsignedValue(v.context().dataspaces, + privateAddress + ps, 4, 0, &errorMessage); + switch (type) { + case 1: + str << SymbolGroupValue::readDouble(v.context().dataspaces, privateAddress + doubleValueOffset); + break; + case 2: + return dumpQStringFromQPrivateClass(v, QPDM_None, stringValueOffset, str); + default: + str << L""; + break; + } + return true; +} + /* Dump QUrl for whose private class no debugging information is available. * Dump the 'originally encoded' byte array of its private class. */ static inline bool dumpQUrl(const SymbolGroupValue &v, std::wostream &str) @@ -2003,6 +2102,15 @@ unsigned dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx, case KT_QUrl: rc = dumpQUrl(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; + case KT_QHostAddress: + rc = dumpQHostAddress(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; + break; + case KT_QProcess: + rc = dumpQProcess(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; + break; + case KT_QScriptValue: + rc = dumpQScriptValue(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; + break; case KT_QString: rc = dumpQString(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.h b/src/libs/qtcreatorcdbext/symbolgroupvalue.h index 81f251faadd..e2374bc4c67 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.h +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.h @@ -137,6 +137,12 @@ public: std::string *errorMessage = 0); static ULONG64 readPointerValue(CIDebugDataSpaces *ds, ULONG64 address, std::string *errorMessage = 0); + static ULONG64 readUnsignedValue(CIDebugDataSpaces *ds, + ULONG64 address, ULONG debuggeeTypeSize, ULONG64 defaultValue = 0, + std::string *errorMessage = 0); + static double readDouble(CIDebugDataSpaces *ds, + ULONG64 address, double defaultValue = 0.0, + std::string *errorMessage = 0); static unsigned pointerSize(); static unsigned intSize(); @@ -168,6 +174,11 @@ struct QtInfo { return QtInfo::prependModuleAndNameSpace(type, coreModule, nameSpace); } std::string prependQtGuiModule(const std::string &type) const { return QtInfo::prependModuleAndNameSpace(type, guiModule, nameSpace); } + std::string prependQtNetworkModule(const std::string &type) const + { return QtInfo::prependModuleAndNameSpace(type, networkModule, nameSpace); } + std::string prependQtScriptModule(const std::string &type) const + { return QtInfo::prependModuleAndNameSpace(type, scriptModule, nameSpace); } + // Prepend module and namespace if missing with some smartness // ('Foo' or -> 'nsp::Foo') => 'QtCored4!nsp::Foo' static std::string prependModuleAndNameSpace(const std::string &type, @@ -177,6 +188,8 @@ struct QtInfo std::string nameSpace; std::string coreModule; std::string guiModule; + std::string networkModule; + std::string scriptModule; // Fully qualified types with module and namespace std::string qObjectType; std::string qObjectPrivateType;