forked from qt-creator/qt-creator
Debugger[CDB]: Simple dumpers for ScriptValue, QHostAddress, QProcess
Dump by reading raw memory for further types for whose private classes no symbolic information is available. Also fix dumping for pointer values, taking CDB's pointer/value duality into account.
This commit is contained in:
@@ -138,6 +138,9 @@ enum KnownType
|
|||||||
KT_QPatternist_AccelTree_BasicNodeData = KT_Qt_Type + KT_Qt_MovableType + 88,
|
KT_QPatternist_AccelTree_BasicNodeData = KT_Qt_Type + KT_Qt_MovableType + 88,
|
||||||
KT_QFile = KT_Qt_Type + KT_HasSimpleDumper + 89,
|
KT_QFile = KT_Qt_Type + KT_HasSimpleDumper + 89,
|
||||||
KT_QDir = KT_Qt_Type + KT_HasSimpleDumper + 90,
|
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
|
// Types: Qt primitive types
|
||||||
KT_QFixed = KT_Qt_Type + KT_Qt_PrimitiveType + 90,
|
KT_QFixed = KT_Qt_Type + KT_Qt_PrimitiveType + 90,
|
||||||
KT_QTextItem = KT_Qt_Type + KT_Qt_PrimitiveType + 91,
|
KT_QTextItem = KT_Qt_Type + KT_Qt_PrimitiveType + 91,
|
||||||
|
|||||||
@@ -218,16 +218,41 @@ ULONG64 SymbolGroupValue::pointerValue(ULONG64 defaultValue) const
|
|||||||
return defaultValue;
|
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<class POD>
|
||||||
|
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,
|
ULONG64 SymbolGroupValue::readPointerValue(CIDebugDataSpaces *ds, ULONG64 address,
|
||||||
std::string *errorMessage /* = 0 */)
|
std::string *errorMessage /* = 0 */)
|
||||||
{
|
{
|
||||||
ULONG64 v = 0;
|
return readPODFromMemory<ULONG64>(ds, address, SymbolGroupValue::pointerSize(), 0, errorMessage);
|
||||||
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;
|
|
||||||
|
ULONG64 SymbolGroupValue::readUnsignedValue(CIDebugDataSpaces *ds,
|
||||||
|
ULONG64 address, ULONG debuggeeTypeSize,
|
||||||
|
ULONG64 defaultValue,
|
||||||
|
std::string *errorMessage /* = 0 */)
|
||||||
|
{
|
||||||
|
return readPODFromMemory<ULONG64>(ds, address, debuggeeTypeSize, defaultValue, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
double SymbolGroupValue::readDouble(CIDebugDataSpaces *ds, ULONG64 address, double defaultValue,
|
||||||
|
std::string *errorMessage /* = 0 */)
|
||||||
|
{
|
||||||
|
return readPODFromMemory<double>(ds, address, sizeof(double), defaultValue, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read memory and return allocated array
|
// 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 qtCoreDefaultModule[] = "QtCored4";
|
||||||
static const char qtGuiDefaultModule[] = "QtGuid4";
|
static const char qtGuiDefaultModule[] = "QtGuid4";
|
||||||
|
static const char qtNetworkDefaultModule[] = "QtNetworkd4";
|
||||||
|
static const char qtScriptDefaultModule[] = "QtScriptd4";
|
||||||
static QtInfo rc;
|
static QtInfo rc;
|
||||||
if (!rc.coreModule.empty())
|
if (!rc.coreModule.empty())
|
||||||
return rc;
|
return rc;
|
||||||
@@ -553,21 +580,23 @@ const QtInfo &QtInfo::get(const SymbolGroupValueContext &ctx)
|
|||||||
// Lookup qstrdup() to hopefully get module (potential libinfix) and namespace
|
// Lookup qstrdup() to hopefully get module (potential libinfix) and namespace
|
||||||
// Typically, this resolves to 'QtGuid4!qstrdup' and 'QtCored4!qstrdup'...
|
// Typically, this resolves to 'QtGuid4!qstrdup' and 'QtCored4!qstrdup'...
|
||||||
const std::string qualifiedSymbol = resolveQtSymbol("qstrdup", qtCoreDefaultModule, "Core", ctx);
|
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.coreModule = qtCoreDefaultModule;
|
||||||
rc.guiModule = qtGuiDefaultModule;
|
rc.guiModule = qtGuiDefaultModule;
|
||||||
|
rc.networkModule = qtNetworkDefaultModule;
|
||||||
|
rc.scriptModule = qtScriptDefaultModule;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Should be 'QtCored4!qstrdup'
|
// 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);
|
rc.coreModule = qualifiedSymbol.substr(0, exclPos);
|
||||||
// Derive other module names 'QtXX<infix>d4'
|
// Derive other module names 'QtXX<infix>d4'
|
||||||
rc.guiModule = rc.coreModule;
|
rc.guiModule = rc.coreModule;
|
||||||
rc.guiModule.replace(0, 6, "QtGui");
|
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'
|
// Any namespace? 'QtCored4!nsp::qstrdup'
|
||||||
const std::string::size_type nameSpaceStart = exclPos + 1;
|
const std::string::size_type nameSpaceStart = exclPos + 1;
|
||||||
const std::string::size_type colonPos = qualifiedSymbol.find(':', nameSpaceStart);
|
const std::string::size_type colonPos = qualifiedSymbol.find(':', nameSpaceStart);
|
||||||
@@ -1070,6 +1099,8 @@ static KnownType knownClassTypeHelper(const std::string &type,
|
|||||||
return KT_QXmltem;
|
return KT_QXmltem;
|
||||||
if (!type.compare(qPos, 8, "QXmlName"))
|
if (!type.compare(qPos, 8, "QXmlName"))
|
||||||
return KT_QXmlName;
|
return KT_QXmlName;
|
||||||
|
if (!type.compare(qPos, 8, "QProcess"))
|
||||||
|
return KT_QProcess;
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 9:
|
||||||
if (!type.compare(qPos, 9, "QBitArray"))
|
if (!type.compare(qPos, 9, "QBitArray"))
|
||||||
@@ -1124,6 +1155,10 @@ static KnownType knownClassTypeHelper(const std::string &type,
|
|||||||
case 12:
|
case 12:
|
||||||
if (!type.compare(qPos, 12, "QKeySequence"))
|
if (!type.compare(qPos, 12, "QKeySequence"))
|
||||||
return KT_QKeySequence;
|
return KT_QKeySequence;
|
||||||
|
if (!type.compare(qPos, 12, "QHostAddress"))
|
||||||
|
return KT_QHostAddress;
|
||||||
|
if (!type.compare(qPos, 12, "QScriptValue"))
|
||||||
|
return KT_QScriptValue;
|
||||||
break;
|
break;
|
||||||
case 13:
|
case 13:
|
||||||
if (!type.compare(qPos, 13, "QTextFragment"))
|
if (!type.compare(qPos, 13, "QTextFragment"))
|
||||||
@@ -1385,8 +1420,10 @@ static ULONG64 addressOfQPrivateMember(const SymbolGroupValue &v, QPrivateDumpMo
|
|||||||
unsigned additionalOffset = 0)
|
unsigned additionalOffset = 0)
|
||||||
{
|
{
|
||||||
std::string errorMessage;
|
std::string errorMessage;
|
||||||
// Dererence d-Ptr.
|
// Dererence d-Ptr (Pointer/Value duality: Value or object address).
|
||||||
ULONG64 dAddress = v.address();
|
ULONG64 dAddress = SymbolGroupValue::isPointerType(v.type()) ? v.pointerValue() : v.address();
|
||||||
|
if (!dAddress)
|
||||||
|
return 0;
|
||||||
if (mode == QPDM_qVirtual) // Skip vtable.
|
if (mode == QPDM_qVirtual) // Skip vtable.
|
||||||
dAddress += SymbolGroupValue::pointerSize();
|
dAddress += SymbolGroupValue::pointerSize();
|
||||||
const ULONG64 dptr = SymbolGroupValue::readPointerValue(v.context().dataspaces,
|
const ULONG64 dptr = SymbolGroupValue::readPointerValue(v.context().dataspaces,
|
||||||
@@ -1412,6 +1449,11 @@ static bool dumpQStringFromQPrivateClass(const SymbolGroupValue &v,
|
|||||||
return false;
|
return false;
|
||||||
const std::string dumpType = QtInfo::get(v.context()).prependQtCoreModule("QString");
|
const std::string dumpType = QtInfo::get(v.context()).prependQtCoreModule("QString");
|
||||||
const std::string symbolName = SymbolGroupValue::pointedToSymbolName(stringAddress , dumpType);
|
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 =
|
SymbolGroupNode *stringNode =
|
||||||
v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage);
|
v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage);
|
||||||
if (!stringNode)
|
if (!stringNode)
|
||||||
@@ -1474,6 +1516,63 @@ static inline bool dumpQFile(const SymbolGroupValue &v, std::wostream &str)
|
|||||||
return dumpQStringFromQPrivateClass(v, QPDM_qVirtual, qIoDevicePrivateSize, 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"<Invalid>";
|
||||||
|
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"<JavaScriptCore>";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Dump QUrl for whose private class no debugging information is available.
|
/* Dump QUrl for whose private class no debugging information is available.
|
||||||
* Dump the 'originally encoded' byte array of its private class. */
|
* Dump the 'originally encoded' byte array of its private class. */
|
||||||
static inline bool dumpQUrl(const SymbolGroupValue &v, std::wostream &str)
|
static inline bool dumpQUrl(const SymbolGroupValue &v, std::wostream &str)
|
||||||
@@ -2003,6 +2102,15 @@ unsigned dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx,
|
|||||||
case KT_QUrl:
|
case KT_QUrl:
|
||||||
rc = dumpQUrl(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
|
rc = dumpQUrl(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
|
||||||
break;
|
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:
|
case KT_QString:
|
||||||
rc = dumpQString(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
|
rc = dumpQString(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -137,6 +137,12 @@ public:
|
|||||||
std::string *errorMessage = 0);
|
std::string *errorMessage = 0);
|
||||||
static ULONG64 readPointerValue(CIDebugDataSpaces *ds, ULONG64 address,
|
static ULONG64 readPointerValue(CIDebugDataSpaces *ds, ULONG64 address,
|
||||||
std::string *errorMessage = 0);
|
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 pointerSize();
|
||||||
static unsigned intSize();
|
static unsigned intSize();
|
||||||
@@ -168,6 +174,11 @@ struct QtInfo
|
|||||||
{ return QtInfo::prependModuleAndNameSpace(type, coreModule, nameSpace); }
|
{ return QtInfo::prependModuleAndNameSpace(type, coreModule, nameSpace); }
|
||||||
std::string prependQtGuiModule(const std::string &type) const
|
std::string prependQtGuiModule(const std::string &type) const
|
||||||
{ return QtInfo::prependModuleAndNameSpace(type, guiModule, nameSpace); }
|
{ 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
|
// Prepend module and namespace if missing with some smartness
|
||||||
// ('Foo' or -> 'nsp::Foo') => 'QtCored4!nsp::Foo'
|
// ('Foo' or -> 'nsp::Foo') => 'QtCored4!nsp::Foo'
|
||||||
static std::string prependModuleAndNameSpace(const std::string &type,
|
static std::string prependModuleAndNameSpace(const std::string &type,
|
||||||
@@ -177,6 +188,8 @@ struct QtInfo
|
|||||||
std::string nameSpace;
|
std::string nameSpace;
|
||||||
std::string coreModule;
|
std::string coreModule;
|
||||||
std::string guiModule;
|
std::string guiModule;
|
||||||
|
std::string networkModule;
|
||||||
|
std::string scriptModule;
|
||||||
// Fully qualified types with module and namespace
|
// Fully qualified types with module and namespace
|
||||||
std::string qObjectType;
|
std::string qObjectType;
|
||||||
std::string qObjectPrivateType;
|
std::string qObjectPrivateType;
|
||||||
|
|||||||
Reference in New Issue
Block a user