/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ // std::copy is perfectly fine, don't let MSVC complain about it being deprecated #pragma warning (disable: 4996) #ifndef NOMINMAX # define NOMINMAX #endif #include "symbolgroupvalue.h" #include "symbolgroup.h" #include "stringutils.h" #include "containers.h" #include "extensioncontext.h" #include #include #include #include #include typedef std::vector::size_type VectorIndexType; typedef std::unordered_map AncestorInfos; static std::unordered_map typeAncestorInfos; /*! \class SymbolGroupValueContext \brief The SymbolGroupValueContext class passes all IDebug interfaces required for SymbolGroupValue. \ingroup qtcreatorcdbext */ /*! \class SymbolGroupValue \brief The SymbolGroupValue class is a flyweight tied to a SymbolGroupNode providing a convenient operator[] (name, index) and value getters for notation of dumpers. Inaccessible members return a SymbolGroupValue in state 'invalid'. Example: \code SymbolGroupValue container(symbolGroupNode, symbolGroupValueContext); if (SymbolGroupValue sizeV = container["d"]["size"]) int size = sizeV.intValue() \endcode \ingroup qtcreatorcdbext */ unsigned SymbolGroupValue::verbose = 0; SymbolGroupValue::SymbolGroupValue(const std::string &parentError) : m_errorMessage(parentError) { if (m_errorMessage.empty()) m_errorMessage = "Invalid"; } SymbolGroupValue::SymbolGroupValue(SymbolGroupNode *node, const SymbolGroupValueContext &ctx) : m_node(node), m_context(ctx) { if (m_node && !m_node->isMemoryAccessible()) { // Invalid if no value m_node = 0; if (SymbolGroupValue::verbose) DebugPrint() << node->name() << '/' << node->iName() << '/' << node->type() << " memory access error"; } } SymbolGroupValue::SymbolGroupValue() : m_errorMessage("Invalid") { } bool SymbolGroupValue::isValid() const { return m_node != 0 && m_context.dataspaces != 0; } // Debug helper static void formatNodeError(const AbstractSymbolGroupNode *n, std::ostream &os) { const AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector &children = n->children(); const VectorIndexType size = children.size(); if (const SymbolGroupNode *sn = n->asSymbolGroupNode()) { os << "type: " << sn->type() << ", raw value: \"" << wStringToString(sn->symbolGroupRawValue()) << "\", 0x" << std::hex << sn->address() << ", " << std::dec; } if (size) { os << "children (" << size << "): ["; for (VectorIndexType i = 0; i < size; ++i) os << ' ' << children.at(i)->name(); os << ']'; } else { os << "No children"; } } SymbolGroupValue SymbolGroupValue::operator[](unsigned index) const { if (ensureExpanded()) if (index < m_node->children().size()) if (SymbolGroupNode *n = m_node->childAt(index)->asSymbolGroupNode()) return SymbolGroupValue(n, m_context); if (isValid() && SymbolGroupValue::verbose) { DebugPrint dp; dp << name() << "::operator[](#" << index << ") failed. "; formatNodeError(m_node, dp); } return SymbolGroupValue(m_errorMessage); } SymbolGroupValue SymbolGroupValue::addSymbolForAncestor(const std::string &ancestorName) const { const SymbolAncestorInfo info = infoOfAncestor(ancestorName); if (info.isValid()) { const ULONG64 base = isPointerType(type()) ? pointerValue() : address(); return addSymbol(base + info.offset, stripClassPrefixes(info.type)); } if (isValid() && SymbolGroupValue::verbose) { // Do not report subsequent errors DebugPrint dp; dp << this->name() << "::addSymbolForAncestor(\"" << ancestorName << "\") failed. "; formatNodeError(m_node, dp); } return SymbolGroupValue(m_errorMessage); } int SymbolGroupValue::readIntegerFromAncestor(const std::string &name, int defaultValue) const { return readPODFromAncestor(name, defaultValue); } ULONG64 SymbolGroupValue::offsetOfChild(const SymbolGroupValue &child) const { const ULONG64 base = isPointerType(type()) ? pointerValue() : address(); const ULONG64 childAddress = child.address(); if (base == 0 || childAddress == 0) return 0; return childAddress - base; } LONG64 SymbolGroupValue::offsetOfAncestor(const std::string &name) const { return infoOfAncestor(name).offset; } ULONG64 SymbolGroupValue::addressOfAncestor(const std::string &name) const { const ULONG64 base = isPointerType(type()) ? pointerValue() : address(); LONG64 offset = offsetOfAncestor(name); return offset >= 0 ? base + ULONG64(offset) : 0; } std::string SymbolGroupValue::typeOfAncestor(const std::string &name) const { return infoOfAncestor(name).type; } SymbolAncestorInfo SymbolGroupValue::infoOfAncestor(const std::string &name) const { const std::string &typeName = type(); AncestorInfos &offsets = typeAncestorInfos[typeName]; auto offsetIt = offsets.find(name); if (offsetIt != offsets.end()) return offsetIt->second; SymbolAncestorInfo info; if (!ensureExpanded()) return info; if (AbstractSymbolGroupNode *abstractChildNode = m_node->childByIName(name.c_str())) { if (SymbolGroupNode *childNode = abstractChildNode->asSymbolGroupNode()) { SymbolGroupValue child(childNode, m_context); ULONG64 childAddress = child.address(); if (childAddress == 0) return info; const ULONG64 base = isPointerType(typeName) ? pointerValue() : address(); info.offset = LONG64(childAddress - base); info.type = child.type(); } } if (!info.isValid()) { // Search recursively for ancestor for (AbstractSymbolGroupNode *abstractChildNode : m_node->children()) { if (SymbolGroupNode *childNode = abstractChildNode->asSymbolGroupNode()) { SymbolGroupValue child(childNode, m_context); if (isPointerType(child.type())) continue; info = child.infoOfAncestor(name); if (info.isValid()) { info.offset += offsetOfChild(child); break; } } } } if (info.isValid()) offsets[name] = info; return info; } SymbolGroupValue SymbolGroupValue::addSymbol(const ULONG64 address, const std::string &type) const { const std::string &pointerToType = pointedToSymbolName(address, type); std::string tmp; if (SymbolGroupNode *ancestorNode = node()->symbolGroup()->addSymbol(module(), pointerToType, "", "", &tmp)) { return SymbolGroupValue(ancestorNode, m_context); } if (isValid() && SymbolGroupValue::verbose) { // Do not report subsequent errors DebugPrint dp; dp << this->name() << "::addSymbol(\"" << address << "\", \"" << address << "\") failed. "; formatNodeError(m_node, dp); } return SymbolGroupValue(m_errorMessage); } unsigned SymbolGroupValue::childCount() const { if (ensureExpanded()) return unsigned(m_node->children().size()); return 0; } SymbolGroupValue SymbolGroupValue::parent() const { if (isValid()) if (AbstractSymbolGroupNode *aParent = m_node->parent()) if (SymbolGroupNode *parent = aParent->asSymbolGroupNode()) return SymbolGroupValue(parent, m_context); return SymbolGroupValue("parent() invoked on invalid value."); } bool SymbolGroupValue::ensureExpanded() const { if (!isValid() || !m_node->canExpand()) return false; if (m_node->isExpanded()) return true; // Set a flag indicating the node was expanded by SymbolGroupValue // and not by an explicit request from the watch model. if (m_node->expand(&m_errorMessage)) return true; if (SymbolGroupValue::verbose) DebugPrint() << "Expand failure of '" << name() << "': " << m_errorMessage; return false; } SymbolGroupValue SymbolGroupValue::operator[](const char *name) const { if (ensureExpanded()) if (AbstractSymbolGroupNode *child = m_node->childByIName(name)) if (SymbolGroupNode *n = child->asSymbolGroupNode()) return SymbolGroupValue(n, m_context); if (isValid() && SymbolGroupValue::verbose) { // Do not report subsequent errors DebugPrint dp; dp << this->name() << "::operator[](\"" << name << "\") failed. "; formatNodeError(m_node, dp); } return SymbolGroupValue(m_errorMessage); } std::string SymbolGroupValue::type() const { return isValid() ? m_node->type() : std::string(); } std::string SymbolGroupValue::name() const { return isValid() ? m_node->name() : std::string(); } unsigned SymbolGroupValue::size() const { return isValid() ? m_node->size() : 0; } std::wstring SymbolGroupValue::value() const { return isValid() ? m_node->symbolGroupFixedValue() : std::wstring(); } double SymbolGroupValue::floatValue(double defaultValue) const { double f = defaultValue; if (isValid()) { std::wistringstream str(value()); str >> f; if (str.fail()) f = defaultValue; } return f; } int SymbolGroupValue::intValue(int defaultValue) const { if (isValid()) { int rc = 0; // Is this an enumeration "EnumValue (0n12)", -> convert to integer std::wstring v = value(); const std::wstring::size_type enPos = v.find(L"(0n"); if (enPos != std::wstring::npos && v.at(v.size() - 1) == L')') v = v.substr(enPos + 3, v.size() - 4); if (integerFromString(wStringToString(v), &rc)) return rc; } if (SymbolGroupValue::verbose) DebugPrint() << name() << '/' << type() << '/'<< m_errorMessage << ": intValue() fails"; return defaultValue; } ULONG64 SymbolGroupValue::pointerValue(ULONG64 defaultValue) const { if (isValid()) { ULONG64 rc = 0; if (integerFromString(wStringToString(value()), &rc)) return rc; } if (SymbolGroupValue::verbose) DebugPrint() << name() << '/'<< type() << '/' << m_errorMessage << ": pointerValue() fails"; 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; } template POD SymbolGroupValue::readPODFromAncestor(const std::string &name, POD defaultValue) const { ULONG64 address = addressOfAncestor(name.c_str()); if (address == 0) return defaultValue; return readPODFromMemory(m_context.dataspaces, address, sizeof(POD), defaultValue, 0); } ULONG64 SymbolGroupValue::readPointerValue(CIDebugDataSpaces *ds, ULONG64 address, std::string *errorMessage /* = 0 */) { return readPODFromMemory(ds, address, SymbolGroupValue::pointerSize(), 0, errorMessage); } ULONG64 SymbolGroupValue::readPointerValueFromAncestor(const std::string &name) const { ULONG64 address = addressOfAncestor(name.c_str()); if (address == 0) return 0; return readPointerValue(m_context.dataspaces, address); } ULONG64 SymbolGroupValue::readUnsignedValue(CIDebugDataSpaces *ds, ULONG64 address, ULONG debuggeeTypeSize, ULONG64 defaultValue, std::string *errorMessage /* = 0 */) { return readPODFromMemory(ds, address, debuggeeTypeSize, defaultValue, errorMessage); } LONG64 SymbolGroupValue::readSignedValue(CIDebugDataSpaces *ds, ULONG64 address, ULONG debuggeeTypeSize, LONG64 defaultValue, std::string *errorMessage /* = 0 */) { return readPODFromMemory(ds, address, debuggeeTypeSize, defaultValue, errorMessage); } // Note: sizeof(int) should always be 4 on Win32/Win64, no need to // differentiate between host and debuggee int types. When implementing // something like 'long', different size on host/debuggee must be taken // into account (shifting signedness bits around). int SymbolGroupValue::readIntValue(CIDebugDataSpaces *ds, ULONG64 address, int defaultValue, std::string *errorMessage /* = 0 */) { return readPODFromMemory(ds, address, sizeof(int), 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 unsigned char *SymbolGroupValue::readMemory(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, std::string *errorMessage /* = 0 */) { unsigned char *buffer = new unsigned char[length]; std::fill(buffer, buffer + length, 0); ULONG received = 0; const HRESULT hr = ds->ReadVirtual(address, buffer, length, &received); if (FAILED(hr)) { delete [] buffer; if (errorMessage) { std::ostringstream estr; estr << "Cannot read " << length << " bytes from 0x" << std::hex << address << ": " << msgDebugEngineComFailed("ReadVirtual", hr); *errorMessage = estr.str(); } return 0; } if (received < length && errorMessage) { std::ostringstream estr; estr << "Warning: Received only " << received << " bytes of " << length << " requested at 0x" << std::hex << address << '.'; *errorMessage = estr.str(); } return buffer; } bool SymbolGroupValue::writeMemory(CIDebugDataSpaces *ds, ULONG64 address, const unsigned char *data, ULONG length, std::string *errorMessage /* =0 */) { ULONG filled = 0; const HRESULT hr = ds->FillVirtual(address, length, const_cast(data), length, &filled); if (FAILED(hr)) { if (errorMessage) { std::ostringstream estr; estr << "Cannot write " << length << " bytes to 0x" << std::hex << address << ": " << msgDebugEngineComFailed("FillVirtual", hr); *errorMessage = estr.str(); } return false; } if (filled < length) { if (errorMessage) { std::ostringstream estr; estr << "Filled only " << filled << " bytes of " << length << " at 0x" << std::hex << address << '.'; *errorMessage = estr.str(); } return false; } return true; } // Return allocated array of data unsigned char *SymbolGroupValue::pointerData(unsigned length) const { if (isValid()) if (const ULONG64 ptr = pointerValue()) return SymbolGroupValue::readMemory(m_context.dataspaces, ptr, length); return 0; } ULONG64 SymbolGroupValue::address() const { if (isValid()) return m_node->address(); return 0; } std::string SymbolGroupValue::module() const { return isValid() ? m_node->module() : std::string(); } // Temporary iname static inline std::string additionalSymbolIname(const SymbolGroup *g) { std::ostringstream str; str << "__additional" << g->root()->children().size(); return str.str(); } SymbolGroupValue SymbolGroupValue::typeCast(const char *type) const { return typeCastedValue(address(), type); } SymbolGroupValue SymbolGroupValue::pointerTypeCast(const char *type) const { return typeCastedValue(pointerValue(), type); } SymbolGroupValue SymbolGroupValue::typeCastedValue(ULONG64 address, const char *type) const { if (!address) return SymbolGroupValue(); const size_t len = strlen(type); if (!len) return SymbolGroupValue(); const bool nonPointer = type[len - 1] != '*'; SymbolGroup *sg = m_node->symbolGroup(); // A bit of magic: For desired pointer types, we can do // 'Foo *' -> '(Foo *)(address)'. // For non-pointers, we need to de-reference: // 'Foo' -> '*(Foo *)(address)' std::ostringstream str; if (nonPointer) str << '*'; str << '(' << type; if (nonPointer) str << " *"; str << ")(" << std::showbase << std::hex << address << ')'; if (SymbolGroupNode *node = sg->addSymbol(module(), str.str(), additionalSymbolIname(sg), &m_errorMessage)) return SymbolGroupValue(node, m_context); return SymbolGroupValue(); } SymbolGroupValue SymbolGroupValue::findMember(const SymbolGroupValue &start, const std::string &symbolName) { const unsigned count = start.childCount(); for (unsigned i = 0; i < count ; ++i) { // check members const SymbolGroupValue child = start[i]; if (child.name() == symbolName) return child; } // Recurse down base classes at the beginning that have class names // as value. for (unsigned i = 0; i < count; ++i) { const SymbolGroupValue child = start[i]; const std::wstring value = child.value(); if (value.compare(0, 6, L"class ") && value.compare(0, 7, L"struct ")) { break; } else { if (SymbolGroupValue r = findMember(child, symbolName)) return r; } } return SymbolGroupValue(); } std::wstring SymbolGroupValue::wcharPointerData(unsigned charCount, unsigned maxCharCount) const { const bool truncated = charCount > maxCharCount; if (truncated) charCount = maxCharCount; if (const unsigned char *ucData = pointerData(charCount * sizeof(wchar_t))) { const wchar_t *utf16Data = reinterpret_cast(ucData); // Be smart about terminating 0-character if (charCount && utf16Data[charCount - 1] == 0) charCount--; std::wstring rc = std::wstring(utf16Data, charCount); if (truncated) rc += L"..."; delete [] ucData; return rc; } return std::wstring(); } std::string SymbolGroupValue::error() const { return m_errorMessage; } // Return number of characters to strip for pointer type unsigned SymbolGroupValue::isPointerType(const std::string &t) { if (endsWith(t, "**")) return 1; if (endsWith(t, " *")) return 2; return 0; } bool SymbolGroupValue::isArrayType(const std::string &t) { return endsWith(t, ']'); } // Return number of characters to strip for pointer type bool SymbolGroupValue::isVTableType(const std::string &t) { const char vtableTypeC[] = "__fptr()"; return t.compare(0, sizeof(vtableTypeC) - 1, vtableTypeC) == 0; } // add pointer type 'Foo' -> 'Foo *', 'Foo *' -> 'Foo **' std::string SymbolGroupValue::pointerType(const std::string &type) { std::string rc = type; if (!endsWith(type, '*')) rc.push_back(' '); rc.push_back('*'); return rc; } unsigned SymbolGroupValue::pointerSize() { static const unsigned ps = SymbolGroupValue::sizeOf("char *"); return ps; } unsigned SymbolGroupValue::intSize() { static const unsigned is = SymbolGroupValue::sizeOf("int"); return is; } unsigned SymbolGroupValue::pointerDiffSize() { static const unsigned is = SymbolGroupValue::sizeOf("ptrdiff_t"); return is; } unsigned SymbolGroupValue::sizeOf(const char *type) { const unsigned rc = GetTypeSize(type); if (!rc && SymbolGroupValue::verbose) DebugPrint() << "GetTypeSize fails for '" << type << '\''; return rc; } unsigned SymbolGroupValue::fieldOffset(const char *type, const char *field) { ULONG rc = 0; if (GetFieldOffset(type, field, &rc)) { if (SymbolGroupValue::verbose) DebugPrint() << "GetFieldOffset fails for '" << type << "' '" << field << '\''; return 0; } return rc; } std::string SymbolGroupValue::stripPointerType(const std::string &t) { // 'Foo *' -> 'Foo', 'Bar **' -> 'Bar *'. if (const unsigned stripLength = isPointerType(t)) return t.substr(0, t.size() - stripLength); return t; } // Strip "class Foo", "struct Bar"-> "Foo", "Bar " std::string SymbolGroupValue::stripClassPrefixes(const std::string &type) { std::string rc = type; if (rc.compare(0, 6, "class ") == 0) { rc.erase(0, 6); } else { if (rc.compare(0, 7, "struct ") == 0) rc.erase(0, 7); } return rc; } // Strip " const" from end of type ("XX const", "XX const *[*]" std::string SymbolGroupValue::stripConst(const std::string &type) { const std::string::size_type constPos = type.rfind(" const"); if (constPos == std::string::npos) return type; // Strip 'const' only if it is at the end 'QString const' // or of some pointer like 'foo ***' std::string rc = type; const std::string::size_type size = rc.size(); std::string::size_type nextPos = constPos + 6; if (nextPos == size) { // Ends with - easy. rc.erase(constPos, nextPos - constPos); return rc; } // Ensure it ends with ' ****'. if (rc.at(nextPos) != ' ') return rc; for (std::string::size_type i = nextPos + 1; i < size; ++i) if (rc.at(i) != '*') return rc; rc.erase(constPos, nextPos - constPos); return rc; } std::string SymbolGroupValue::addPointerType(const std::string &t) { // 'char' -> 'char *' -> 'char **' std::string rc = t; if (!endsWith(rc, '*')) rc.push_back(' '); rc.push_back('*'); return rc; } std::string SymbolGroupValue::stripArrayType(const std::string &t) { const std::string::size_type bracket = t.rfind('['); if (bracket != std::string::npos) { std::string rc = t.substr(0, bracket); trimBack(rc); return rc; } return t; } std::string SymbolGroupValue::stripModuleFromType(const std::string &type) { const std::string::size_type exclPos = type.find('!'); return exclPos != std::string::npos ? type.substr(exclPos + 1, type.size() - exclPos - 1) : type; } std::string SymbolGroupValue::moduleOfType(const std::string &type) { const std::string::size_type exclPos = type.find('!'); return exclPos != std::string::npos ? type.substr(0, exclPos) : std::string(); } // Symbol Name/(Expression) of a pointed-to instance ('Foo' at 0x10') ==> '*(Foo *)0x10' std::string SymbolGroupValue::pointedToSymbolName(ULONG64 address, const std::string &type) { std::ostringstream str; str << "*(" << type; if (!endsWith(type, '*')) str << ' '; str << "*)" << std::showbase << std::hex << address; return str.str(); } /*! \class QtInfo \brief The QtInfo class provides Qt information determined on demand. Namespace, modules, and basic class names containing the module for fast lookup. \ingroup qtcreatorcdbext */ const QtInfo &QtInfo::get(const SymbolGroupValueContext &ctx) { static QtInfo rc; if (!rc.libInfix.empty()) return rc; typedef std::list StringList; typedef StringList::const_iterator StringListConstIt; std::string moduleName; std::string::size_type exclPos = std::string::npos; std::string::size_type libPos = std::string::npos; std::string::size_type qtPos = std::string::npos; const StringList &modules = SymbolGroupValue::getAllModuleNames(ctx); for (StringListConstIt module = modules.begin(), total = modules.end(); module != total; ++module) { moduleName = *module; qtPos = moduleName.find("Qt"); if (qtPos != std::string::npos) { libPos = moduleName.find("Core"); if (libPos != std::string::npos && (libPos - qtPos) < 4) break; } } if (libPos == std::string::npos) moduleName.clear(); else moduleName += '!'; const char* qstrdupSymbol = "*qstrdup"; std::string qualifiedSymbol; do { // Lookup qstrdup() to get the core module const std::string pattern = moduleName + qstrdupSymbol; const StringList &allMatches = SymbolGroupValue::resolveSymbolName(pattern.c_str(), ctx); if (!allMatches.empty()) { qualifiedSymbol = allMatches.front(); } else { if (!moduleName.empty()) { // If there is no qstrdup symbol in the module fall back to a global lookup. moduleName.clear(); continue; } } exclPos = qualifiedSymbol.find('!'); libPos = qualifiedSymbol.find("Core"); if (exclPos != std::string::npos) { if (!moduleName.empty()) { // found the core module // determine the qt version from the module name if (isdigit(qualifiedSymbol.at(2))) rc.version = qualifiedSymbol.at(2) - '0'; else rc.version = qualifiedSymbol.at(exclPos - 1) - '0'; rc.libInfix = qualifiedSymbol.substr(libPos + 4, exclPos - libPos - 4); } else { // Found the Qt symbol but in an unexpected module, most probably // it is a static build. rc.isStatic = true; rc.libInfix = qualifiedSymbol.substr(0, exclPos); // The Qt version cannot be determined by the module name. Looking up // qInstallMessageHandler which is in Qt since 5.0 to determine the version. const std::string pattern = rc.libInfix + "!*qInstallMessageHandler"; const StringList &allMatches = SymbolGroupValue::resolveSymbolName(pattern.c_str(), ctx); const StringListConstIt match = std::find_if(allMatches.begin(), allMatches.end(), SubStringPredicate(rc.libInfix.c_str())); rc.version = match == allMatches.end() ? 4 : 5; } // Any namespace? 'QtCored4!nsp::qstrdup' const std::string::size_type nameSpaceStart = exclPos + 1; const std::string::size_type colonPos = qualifiedSymbol.find(':', nameSpaceStart); if (colonPos != std::string::npos) rc.nameSpace = qualifiedSymbol.substr(nameSpaceStart, colonPos - nameSpaceStart); } else { // Can't find a basic Qt symbol so use a fallback rc.libInfix = "d4"; rc.version = 4; } } while (false); rc.qObjectType = rc.prependQtCoreModule("QObject"); rc.qObjectPrivateType = rc.prependQtCoreModule("QObjectPrivate"); rc.qWindowPrivateType = rc.prependQtGuiModule("QWindowPrivate"); rc.qWidgetPrivateType = rc.prependQtModule("QWidgetPrivate", rc.version >= 5 ? Widgets : Gui); if (SymbolGroupValue::verbose) DebugPrint() << rc; return rc; } std::string QtInfo::moduleName(Module m) const { if (isStatic) return libInfix; // Must match the enumeration static const char* modNames[] = {"Core", "Gui", "Widgets", "Network", "Script", "Qml" }; std::ostringstream result; result << "Qt"; if (version >= 5) result << version; result << modNames[m] << libInfix; return result.str(); } std::string QtInfo::prependModuleAndNameSpace(const std::string &type, const std::string &module, const std::string &aNameSpace) { // Strip the prefixes "class ", "struct ". std::string rc = SymbolGroupValue::stripClassPrefixes(type); // Is the namespace 'nsp::' missing? if (!aNameSpace.empty()) { const bool nameSpaceMissing = rc.size() <= aNameSpace.size() || rc.compare(0, aNameSpace.size(), aNameSpace) != 0 || rc.at(aNameSpace.size()) != ':'; if (nameSpaceMissing) { rc.insert(0, "::"); rc.insert(0, aNameSpace); } } // Is the module 'Foo!' missing? if (!module.empty()) { const bool moduleMissing = rc.size() <= module.size() || rc.compare(0, module.size(), module) != 0 || rc.at(module.size()) != '!'; if (moduleMissing) { rc.insert(0, 1, '!'); rc.insert(0, module); } } return rc; } int QtInfo::qtTypeInfoVersion(const SymbolGroupValueContext &ctx) { const QtInfo qtInfo = QtInfo::get(ctx); const std::string &hookDataSymbolName = qtInfo.prependQtCoreModule("qtHookData"); ULONG64 offset = 0; if (FAILED(ctx.symbols->GetOffsetByName(hookDataSymbolName.c_str(), &offset))) return 0; ULONG64 hookVer = SymbolGroupValue::readPointerValue(ctx.dataspaces, offset); if (hookVer < 3) return 0; offset += 6 * SymbolGroupValue::pointerSize(); return static_cast(SymbolGroupValue::readPointerValue(ctx.dataspaces, offset)); } std::ostream &operator<<(std::ostream &os, const QtInfo &i) { os << "Qt Info: Version: " << i.version << " Modules '" << i.moduleName(QtInfo::Core) << "', '" << i.moduleName(QtInfo::Gui) << "', Namespace='" << i.nameSpace << "', types: " << i.qObjectType << ',' << i.qObjectPrivateType << ',' << i.qWidgetPrivateType; return os; } std::list SymbolGroupValue::resolveSymbolName(const char *pattern, const SymbolGroupValueContext &c, std::string *errorMessage /* = 0 */) { // Extract the names const SymbolList symbols = resolveSymbol(pattern, c, errorMessage); std::list rc; if (!symbols.empty()) { const SymbolList::const_iterator cend = symbols.end(); for (SymbolList::const_iterator it = symbols.begin(); it != cend; ++it) rc.push_back(it->first); } return rc; } SymbolGroupValue::SymbolList SymbolGroupValue::resolveSymbol(const char *pattern, const SymbolGroupValueContext &c, std::string *errorMessage /* = 0 */) { enum { bufSize = 2048 }; std::list rc; if (errorMessage) errorMessage->clear(); // Is it an incomplete symbol? if (!pattern[0]) return rc; ULONG64 handle = 0; // E_NOINTERFACE means "no match". Apparently, it does not always // set handle. HRESULT hr = c.symbols->StartSymbolMatch(pattern, &handle); if (hr == E_NOINTERFACE) { if (handle) c.symbols->EndSymbolMatch(handle); return rc; } if (FAILED(hr)) { if (errorMessage) *errorMessage= msgDebugEngineComFailed("StartSymbolMatch", hr); return rc; } char buf[bufSize]; ULONG64 offset; while (true) { hr = c.symbols->GetNextSymbolMatch(handle, buf, bufSize - 1, 0, &offset); if (hr == E_NOINTERFACE) break; if (hr == S_OK) rc.push_back(Symbol(std::string(buf), offset)); } c.symbols->EndSymbolMatch(handle); return rc; } std::list SymbolGroupValue::getAllModuleNames(const SymbolGroupValueContext &c, std::string *errorMessage) { enum { bufSize = 2048 }; std::list rc; ULONG moduleCount = 0; ULONG unloaded = 0; HRESULT hr = c.symbols->GetNumberModules(&moduleCount, &unloaded); if (FAILED(hr)) { if (errorMessage) *errorMessage = msgDebugEngineComFailed("GetNumberModules", hr); return rc; } moduleCount += unloaded; // lookup module names char moduleBuffer[bufSize]; for (ULONG index = 0; index < moduleCount; ++index) { hr = c.symbols->GetModuleNameString(DEBUG_MODNAME_MODULE, index, NULL, moduleBuffer, bufSize - 1, NULL); if (FAILED(hr)) { if (errorMessage) *errorMessage = msgDebugEngineComFailed("GetNumberModules", hr); return rc; } rc.push_back(moduleBuffer); } return rc; } // Resolve a type, that is, obtain its module name ('QString'->'QtCored4!QString') std::string SymbolGroupValue::resolveType(const std::string &typeIn, const SymbolGroupValueContext &ctx, const std::string ¤tModule /* = "" */) { enum { BufSize = 512 }; if (typeIn.empty() || typeIn.find('!') != std::string::npos) return typeIn; const std::string stripped = SymbolGroupValue::stripClassPrefixes(typeIn); // Use the module of the current symbol group for templates. // This is because resolving some template types (std::list<> has been // observed to result in 'QtGui4d!std::list', which subsequently fails. if (!currentModule.empty() && stripped.find('<') != std::string::npos) { std::string trc = currentModule; trc.push_back('!'); trc += stripped; return trc; } // Obtain the base address of the module using an obscure ioctl() call. // See inline implementation of GetTypeSize() and docs. SYM_DUMP_PARAM symParameters = { sizeof (SYM_DUMP_PARAM), (PUCHAR)stripped.c_str(), DBG_DUMP_NO_PRINT, 0, NULL, NULL, NULL, 0, NULL }; const ULONG typeSize = Ioctl(IG_GET_TYPE_SIZE, &symParameters, symParameters.size); if (!typeSize || !symParameters.ModBase) // Failed? return stripped; const std::string module = moduleNameByOffset(ctx.symbols, symParameters.ModBase); if (module.empty()) return stripped; std::string rc = module; rc.push_back('!'); rc += stripped; return rc; } // get the inner types: "QMap" -> "int", "double" std::vector SymbolGroupValue::innerTypesOf(const std::string &t) { std::vector rc; std::string::size_type pos = t.find('<'); if (pos == std::string::npos) return rc; rc.reserve(5); const std::string::size_type size = t.size(); // Record all elements of level 1 to work correctly for // 'std::map >' unsigned level = 0; std::string::size_type start = 0; for ( ; pos < size ; pos++) { const char c = t.at(pos); switch (c) { case '<': if (++level == 1) start = pos + 1; break; case '>': if (--level == 0) { // last element std::string innerType = t.substr(start, pos - start); trimFront(innerType); trimBack(innerType); rc.push_back(innerType); return rc; } break; case ',': if (level == 1) { // std::map: start anew at ','. std::string innerType = t.substr(start, pos - start); trimFront(innerType); trimBack(innerType); rc.push_back(innerType); start = pos + 1; } break; } } return rc; } std::ostream &operator<<(std::ostream &str, const SymbolGroupValue &v) { if (v) { str << '\'' << v.name() << "' 0x" << std::hex << v.address() << std::dec << ' ' << v.type() << ": '" << wStringToString(v.value()) << '\''; } else { str << "Invalid value '" << v.error() << '\''; } return str; } // -------------------- Simple dumping helpers // Courtesy of qdatetime.cpp static inline void getDateFromJulianDay(unsigned julianDay, int *year, int *month, int *day) { int y, m, d; if (julianDay >= 2299161) { typedef unsigned long long qulonglong; // Gregorian calendar starting from October 15, 1582 // This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern qulonglong ell, n, i, j; ell = qulonglong(julianDay) + 68569; n = (4 * ell) / 146097; ell = ell - (146097 * n + 3) / 4; i = (4000 * (ell + 1)) / 1461001; ell = ell - (1461 * i) / 4 + 31; j = (80 * ell) / 2447; d = int(ell - (2447 * j) / 80); ell = j / 11; m = int(j + 2 - (12 * ell)); y = int(100 * (n - 49) + i + ell); } else { // Julian calendar until October 4, 1582 // Algorithm from Frequently Asked Questions about Calendars by Claus Toendering julianDay += 32082; int dd = (4 * julianDay + 3) / 1461; int ee = julianDay - (1461 * dd) / 4; int mm = ((5 * ee) + 2) / 153; d = ee - (153 * mm + 2) / 5 + 1; m = mm + 3 - 12 * (mm / 10); y = dd - 4800 + (mm / 10); if (y <= 0) --y; } if (year) *year = y; if (month) *month = m; if (day) *day = d; } const char *stdStringTypeC = "std::basic_string,std::allocator >"; const char *stdWStringTypeC = "std::basic_string,std::allocator >"; // Compiler option: -Zc:wchar_t-: const char *stdWStringWCharTypeC = "std::basic_string,std::allocator >"; static KnownType knownPODTypeHelper(const std::string &type, std::string::size_type endPos) { if (type.empty() || !endPos) return KT_Unknown; // Strip pointer types. const bool isPointer = type.at(endPos - 1) == '*'; if (isPointer) { endPos--; if (endPos > 0 && type.at(endPos - 1) == ' ') endPos--; } switch (type.at(0)) { case 'c': if (endPos == 4 && !type.compare(0, endPos, "char")) return isPointer ? KT_POD_PointerType : KT_Char; break; case 'd': if (endPos == 6 && !type.compare(0, endPos, "double")) return isPointer ? KT_POD_PointerType : KT_FloatType; break; case 'f': if (endPos == 5 && !type.compare(0, endPos, "float")) return isPointer ? KT_POD_PointerType : KT_FloatType; break; case 'l': if (endPos >= 4 && !type.compare(0, 4, "long")) if (endPos == 4 || type.at(4) == ' ') return isPointer ? KT_POD_PointerType : KT_IntType; break; case 'i': // 'int' 'int64' if (endPos >= 3 && !type.compare(0, 3, "int")) if (endPos == 3 || type.at(3) == ' ' || type.at(3) == '6') return isPointer ? KT_POD_PointerType : KT_IntType; break; case 's': if (endPos == 5 && !type.compare(0, 5, "short")) return isPointer ? KT_POD_PointerType : KT_IntType; if (endPos >= 6 && !type.compare(0, 6, "signed")) if (endPos == 6 || type.at(6) == ' ') return isPointer ? KT_POD_PointerType : KT_IntType; break; case 'u': if (endPos >= 8 && !type.compare(0, 8, "unsigned")) { if (endPos == 8 || type.at(8) == ' ') { if (isPointer) return KT_POD_PointerType; return type.compare(0, 13, "unsigned char") ? KT_UnsignedIntType : KT_UnsignedChar; } } break; } return isPointer ? KT_PointerType : KT_Unknown; } // Determine type starting from a position (with/without 'class '/'struct ' prefix). static KnownType knownClassTypeHelper(const std::string &type, std::string::size_type pos, std::string::size_type endPos) { // STL ? const std::wstring::size_type templatePos = type.find('<', pos); static const std::wstring::size_type stlClassLen = 5; if (!type.compare(pos, stlClassLen, "std::")) { // STL containers const std::wstring::size_type hPos = pos + stlClassLen; if (templatePos != std::string::npos) { switch (templatePos - stlClassLen - pos) { case 3: if (!type.compare(hPos, 3, "set")) return KT_StdSet; if (!type.compare(hPos, 3, "map")) return KT_StdMap; break; case 4: if (!type.compare(hPos, 4, "list")) return KT_StdList; break; case 5: if (!type.compare(hPos, 5, "stack")) return KT_StdStack; if (!type.compare(hPos, 5, "deque")) return KT_StdDeque; if (!type.compare(hPos, 5, "array")) return KT_StdArray; break; case 6: if (!type.compare(hPos, 6, "vector")) return KT_StdVector; break; case 7: if (!type.compare(hPos, 7, "complex")) return KT_StdComplex; break; case 8: if (!type.compare(hPos, 8, "multimap")) return KT_StdMultiMap; if (!type.compare(hPos, 8, "multiset")) return KT_StdMultiSet; if (!type.compare(hPos, 8, "valarray")) return KT_StdValArray; break; case 13: if (!type.compare(hPos, 13, "unordered_map")) return KT_StdUnorderedMap; if (!type.compare(hPos, 13, "unordered_set")) return KT_StdUnorderedSet; break; case 18: if (!type.compare(hPos, 18, "unordered_multimap")) return KT_StdUnorderedMultiMap; if (!type.compare(hPos, 18, "unordered_multiset")) return KT_StdUnorderedMultiSet; break; } } // STL strings if (!type.compare(pos, endPos - pos, stdStringTypeC)) return KT_StdString; if (!type.compare(pos, endPos - pos, stdWStringTypeC) || !type.compare(pos, endPos - pos, stdWStringWCharTypeC)) return KT_StdWString; return KT_Unknown; } // std::sth // Check for a 'Q' past the last namespace (beware of namespaced Qt: // 'nsp::QString'). const std::wstring::size_type lastNameSpacePos = type.rfind(':', templatePos); const std::wstring::size_type qPos = lastNameSpacePos == std::string::npos ? type.find('Q', pos) : lastNameSpacePos + 1; if (qPos == std::string::npos || qPos >= type.size() || type.at(qPos) != 'Q') return KT_Unknown; // Qt types (templates) if (templatePos != std::string::npos) { // Do not fall for QMap::iterator, which is actually an inner class. if (endPos > templatePos && type.at(endPos - 1) != '>') return KT_Unknown; switch (templatePos - qPos) { case 4: if (!type.compare(qPos, 4, "QSet")) return KT_QSet; if (!type.compare(qPos, 4, "QMap")) return KT_QMap; break; case 5: if (!type.compare(qPos, 5, "QHash")) return KT_QHash; if (!type.compare(qPos, 5, "QList")) return KT_QList; break; case 6: if (!type.compare(qPos, 6, "QFlags")) return KT_QFlags; if (!type.compare(qPos, 6, "QStack")) return KT_QStack; if (!type.compare(qPos, 6, "QQueue")) return KT_QQueue; break; case 7: if (!type.compare(qPos, 7, "QVector")) return KT_QVector; break; case 9: if (!type.compare(qPos, 9, "QMultiMap")) return KT_QMultiMap; break; case 10: if (!type.compare(qPos, 10, "QMultiHash")) return KT_QMultiHash; break; case 11: if (!type.compare(qPos, 11, "QLinkedList")) return KT_QLinkedList; break; case 12: if (!type.compare(qPos, 12, "QWeakPointer")) return KT_QWeakPointer; break; case 14: if (!type.compare(qPos, 14, "QSharedPointer")) return KT_QSharedPointer; break; } } // Remaining non-template types switch (endPos - qPos) { case 4: if (!type.compare(qPos, 4, "QPen")) return KT_QPen; if (!type.compare(qPos, 4, "QUrl")) return KT_QUrl; if (!type.compare(qPos, 4, "QDir")) return KT_QDir; break; case 5: if (!type.compare(qPos, 5, "QChar")) return KT_QChar; if (!type.compare(qPos, 5, "QDate")) return KT_QDate; if (!type.compare(qPos, 5, "QTime")) return KT_QTime; if (!type.compare(qPos, 5, "QSize")) return KT_QSize; if (!type.compare(qPos, 5, "QLine")) return KT_QLine; if (!type.compare(qPos, 5, "QRect")) return KT_QRect; if (!type.compare(qPos, 5, "QIcon")) return KT_QIcon; if (!type.compare(qPos, 5, "QFile")) return KT_QFile; break; case 6: if (!type.compare(qPos, 6, "QColor")) return KT_QColor; if (!type.compare(qPos, 6, "QSizeF")) return KT_QSizeF; if (!type.compare(qPos, 6, "QPoint")) return KT_QPoint; if (!type.compare(qPos, 6, "QLineF")) return KT_QLineF; if (!type.compare(qPos, 6, "QRectF")) return KT_QRectF; if (!type.compare(qPos, 6, "QBrush")) return KT_QBrush; if (!type.compare(qPos, 6, "QImage")) return KT_QImage; if (!type.compare(qPos, 6, "QFixed")) return KT_QFixed; break; case 7: if (!type.compare(qPos, 7, "QString")) return KT_QString; if (!type.compare(qPos, 7, "QPointF")) return KT_QPointF; if (!type.compare(qPos, 7, "QObject")) return KT_QObject; if (!type.compare(qPos, 7, "QWidget")) return KT_QWidget; if (!type.compare(qPos, 7, "QWindow")) return KT_QWindow; if (!type.compare(qPos, 7, "QLocale")) return KT_QLocale; if (!type.compare(qPos, 7, "QMatrix")) return KT_QMatrix; if (!type.compare(qPos, 7, "QRegExp")) return KT_QRegExp; if (!type.compare(qPos, 7, "QRegion")) return KT_QRegion; if (!type.compare(qPos, 7, "QPixmap")) return KT_QPixmap; break; case 8: if (!type.compare(qPos, 8, "QVariant")) return KT_QVariant; if (!type.compare(qPos, 8, "QMargins")) return KT_QMargins; if (!type.compare(qPos, 8, "QXmlItem")) 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")) return KT_QBitArray; if (!type.compare(qPos, 9, "QDateTime")) return KT_QDateTime; if (!type.compare(qPos, 9, "QFileInfo")) return KT_QFileInfo; if (!type.compare(qPos, 9, "QMetaEnum")) return KT_QMetaEnum; if (!type.compare(qPos, 9, "QTextItem")) return KT_QTextItem; if (!type.compare(qPos, 9, "QTimeZone")) return KT_QTimeZone; if (!type.compare(qPos, 9, "QVector2D")) return KT_QVector2D; if (!type.compare(qPos, 9, "QVector3D")) return KT_QVector3D; if (!type.compare(qPos, 9, "QVector4D")) return KT_QVector4D; break; case 10: if (!type.compare(qPos, 10, "QAtomicInt")) return KT_QAtomicInt; if (!type.compare(qPos, 10, "QByteArray")) return KT_QByteArray; if (!type.compare(qPos, 10, "QMatrix4x4")) return KT_QMatrix4x4; if (!type.compare(qPos, 10, "QTextBlock")) return KT_QTextBlock; if (!type.compare(qPos, 10, "QTransform")) return KT_QTransform; if (!type.compare(qPos, 10, "QFixedSize")) return KT_QFixedSize; if (!type.compare(qPos, 10, "QStringRef")) return KT_QStringRef; break; case 11: if (!type.compare(qPos, 11, "QStringList")) return KT_QStringList; if (!type.compare(qPos, 11, "QBasicTimer")) return KT_QBasicTimer; if (!type.compare(qPos, 11, "QMetaMethod")) return KT_QMetaMethod; if (!type.compare(qPos, 11, "QModelIndex")) return KT_QModelIndex; if (!type.compare(qPos, 11, "QQuaternion")) return KT_QQuaternion; if (!type.compare(qPos, 11, "QScriptItem")) return KT_QScriptItem; if (!type.compare(qPos, 11, "QFixedPoint")) return KT_QFixedPoint; if (!type.compare(qPos, 11, "QScriptLine")) return KT_QScriptLine; if (!type.compare(qPos, 11, "QTextCursor")) return KT_QTextCursor; break; 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, "QIPv6Address")) return KT_QIPv6Address; if (!type.compare(qPos, 12, "QScriptValue")) return KT_QScriptValue; break; case 13: if (!type.compare(qPos, 13, "QTextFragment")) return KT_QTextFragment; if (!type.compare(qPos, 13, "QTreeViewItem")) return KT_QTreeViewItem; break; case 14: if (!type.compare(qPos, 14, "QMetaClassInfo")) return KT_QMetaClassInfo; if (!type.compare(qPos, 14, "QNetworkCookie")) return KT_QNetworkCookie; break; case 15: if (!type.compare(qPos, 15, "QBasicAtomicInt")) return KT_QBasicAtomicInt; if (!type.compare(qPos, 15, "QHashDummyValue")) return KT_QHashDummyValue; if (!type.compare(qPos, 15, "QSourceLocation")) return KT_QSourceLocation; if (!type.compare(qPos, 15, "QScriptAnalysis")) return KT_QScriptAnalysis; break; case 16: if (!type.compare(qPos, 16, "QTextUndoCommand")) return KT_QTextUndoCommand; break; case 18: if (!type.compare(qPos, 18, "QNetworkProxyQuery")) return KT_QNetworkProxyQuery; if (!type.compare(qPos, 18, "QXmlNodeModelIndex")) return KT_QXmlNodeModelIndex; break; case 19: if (!type.compare(qPos, 19, "QItemSelectionRange")) return KT_QItemSelectionRange; if (!type.compare(qPos, 19, "QPaintBufferCommand")) return KT_QPaintBufferCommand; if (!type.compare(qPos, 19, "QTextHtmlParserNode")) return KT_QTextHtmlParserNode; if (!type.compare(qPos, 19, "QXmlStreamAttribute")) return KT_QXmlStreamAttribute; if (!type.compare(qPos, 19, "QGlyphJustification")) return KT_QGlyphJustification; break; case 20: if (!type.compare(qPos, 20, "QTextBlock::iterator")) return KT_QTextBlock_iterator; if (!type.compare(qPos, 20, "QTextFrame::iterator")) return KT_QTextFrame_iterator; break; case 21: if (!type.compare(qPos, 21, "QPersistentModelIndex")) return KT_QPersistentModelIndex; if (!type.compare(qPos, 21, "QPainterPath::Element")) return KT_QPainterPath_Element; break; case 22: if (!type.compare(qPos, 22, "QObjectPrivate::Sender")) return KT_QObjectPrivate_Sender; break; case 24: if (!type.compare(qPos, 24, "QPatternist::AtomicValue")) return KT_QPatternist_AtomicValue; if (!type.compare(qPos, 24, "QPatternist::Cardinality")) return KT_QPatternist_Cardinality; break; case 26: if (!type.compare(qPos, 26, "QObjectPrivate::Connection")) return KT_QObjectPrivate_Connection; if (!type.compare(qPos, 26, "QPatternist::ItemCacheCell")) return KT_QPatternist_ItemCacheCell; if (!type.compare(qPos, 26, "QPatternist::ItemType::Ptr")) return KT_QPatternist_ItemType_Ptr; if (!type.compare(qPos, 26, "QPatternist::NamePool::Ptr")) return KT_QPatternist_NamePool_Ptr; break; case 27: if (!type.compare(qPos, 27, "QXmlStreamEntityDeclaration")) return KT_QXmlStreamEntityDeclaration; break; case 28: if (!type.compare(qPos, 28, "QPatternist::Expression::Ptr")) return KT_QPatternist_Expression_Ptr; break; case 29: if (!type.compare(qPos, 29, "QXmlStreamNotationDeclaration")) return KT_QXmlStreamNotationDeclaration; break; case 30: if (!type.compare(qPos, 30, "QPatternist::SequenceType::Ptr")) return KT_QPatternist_SequenceType_Ptr; if (!type.compare(qPos, 30, "QXmlStreamNamespaceDeclaration")) return KT_QXmlStreamNamespaceDeclaration; break; case 32: break; if (!type.compare(qPos, 32, "QPatternist::Item::Iterator::Ptr")) return KT_QPatternist_Item_Iterator_Ptr; case 34: break; if (!type.compare(qPos, 34, "QPatternist::ItemSequenceCacheCell")) return KT_QPatternist_ItemSequenceCacheCell; case 37: break; if (!type.compare(qPos, 37, "QNetworkHeadersPrivate::RawHeaderPair")) return KT_QNetworkHeadersPrivate_RawHeaderPair; if (!type.compare(qPos, 37, "QPatternist::AccelTree::BasicNodeData")) return KT_QPatternist_AccelTree_BasicNodeData; break; } return KT_Unknown; } KnownType knownType(const std::string &type, unsigned flags) { if (type.empty()) return KT_Unknown; // Autostrip one pointer if desired const std::string::size_type endPos = (flags & KnownTypeAutoStripPointer) ? type.size() - SymbolGroupValue::isPointerType(type) : type.size(); // PODs first const KnownType podType = knownPODTypeHelper(type, endPos); if (podType != KT_Unknown) return podType; if (flags & KnownTypeHasClassPrefix) { switch (type.at(0)) { // Check 'class X' or 'struct X' case 'c': if (!type.compare(0, 6, "class ")) return knownClassTypeHelper(type, 6, endPos); break; case 's': if (!type.compare(0, 7, "struct ")) return knownClassTypeHelper(type, 7, endPos); break; } } else { // No prefix, full check return knownClassTypeHelper(type, 0, endPos); } return KT_Unknown; } void formatKnownTypeFlags(std::ostream &os, KnownType kt) { switch (kt) { case KT_Unknown: os << ""; return; case KT_POD_PointerType: os << " pod_pointer"; break; case KT_PointerType: os << " pointer"; break; default: break; } if (kt & KT_POD_Type) os << " pod"; if (kt & KT_Qt_Type) os << " qt"; if (kt & KT_Qt_PrimitiveType) os << " qt_primitive"; if (kt & KT_Qt_MovableType) os << " qt_movable"; if (kt & KT_ContainerType) os << " container"; if (kt & KT_STL_Type) os << " stl"; if (kt & KT_HasSimpleDumper) os << " simple_dumper"; } unsigned SymbolGroupValue::isMovable(const std::string &t, const SymbolGroupValue &v) { KnownType kt = knownType(t, false); if (kt & (KT_POD_Type | KT_Qt_MovableType | KT_Qt_PrimitiveType)) return true; return kt == KT_QStringList && QtInfo::get(v.context()).version >= 5; } static inline DumpParameterRecodeResult checkCharArrayRecode(const SymbolGroupValue &v) { return DumpParameters::checkRecode(v.type(), std::string(), v.value(), v.context(), v.address()); } // Helper struct containing data Address and size/alloc information // from Qt's QString/QByteArray. struct QtStringAddressData { QtStringAddressData() : address(0), size(0), allocated(0) {} ULONG64 address; unsigned size; // Size and allocated are in ushort for QString's. unsigned allocated; }; /* Helper to determine the location and size of the data of * QStrings/QByteArrays for versions 4,5. In Qt 4, 'd' has a 'data' * pointer. In Qt 5, the d-elements and the data are in a storage pool * and the data are at an offset behind the d-structures (QString, * QByteArray, QVector). */ QtStringAddressData readQtStringAddressData(const SymbolGroupValue &dV, const QtInfo &qtInfo) { QtStringAddressData result; const std::string &arrayData = qtInfo.prependModuleAndNameSpace("QArrayData", std::string(), qtInfo.nameSpace); const SymbolGroupValue adV = qtInfo.version < 5 ? dV : dV[arrayData.c_str()]; if (!adV) return QtStringAddressData(); const SymbolGroupValue sizeV = adV["size"]; const SymbolGroupValue allocV = adV["alloc"]; if (!sizeV || !allocV) return QtStringAddressData(); result.size = sizeV.intValue(); result.allocated = allocV.intValue(); if (qtInfo.version < 5) { // Qt 4: Simple 'data' pointer. result.address = adV["data"].pointerValue(); } else { // Qt 5: Memory pool after the data element. const SymbolGroupValue offsetV = adV["offset"]; if (!offsetV) return QtStringAddressData(); // Take the address for QTypeArrayData of QByteArray, else // pointer value of D-pointer. const ULONG64 baseAddress = SymbolGroupValue::isPointerType(adV.type()) ? adV.pointerValue() : adV.address(); result.address = baseAddress + offsetV.intValue(); } return result; } // Retrieve data from a QByteArrayData(char)/QStringData(wchar_t) // in desired type. For empty arrays, no data are allocated. // All sizes are in CharType units. zeroTerminated means data are 0-terminated // in the data type, but "size" does not contain it. template bool readQt5StringData(const SymbolGroupValue &dV, const QtInfo &qtInfo, bool zeroTerminated, unsigned position, unsigned sizeLimit, unsigned *fullSize, unsigned *arraySize, CharType **array) { *array = 0; const QtStringAddressData data = readQtStringAddressData(dV, qtInfo); if (!data.address || position > data.size) return false; const ULONG64 address = data.address + sizeof(CharType) * position; *fullSize = data.size - position; *arraySize = std::min(*fullSize, sizeLimit); if (!*fullSize) return true; const unsigned memorySize = sizeof(CharType) * (*arraySize + (zeroTerminated ? 1 : 0)); unsigned char *memory = SymbolGroupValue::readMemory(dV.context().dataspaces, address, memorySize); if (!memory) return false; *array = reinterpret_cast(memory); if ((*arraySize <= *fullSize) && zeroTerminated) *(*array + *arraySize) = CharType(0); return true; } static inline bool dumpQString(const SymbolGroupValue &v, std::wostream &str, MemoryHandle **memoryHandle = 0, unsigned position = 0, unsigned length = std::numeric_limits::max()) { const QtInfo &qtInfo = QtInfo::get(v.context()); const SymbolGroupValue dV = v["d"]; if (!dV) return false; // Qt 4. if (qtInfo.version < 5) { if (const SymbolGroupValue sizeValue = dV["size"]) { const int size = sizeValue.intValue(); if (size >= 0) { std::wstring stringData = dV["data"].wcharPointerData(size); if (position && position < stringData.size()) stringData.erase(0, position); if (length < stringData.size()) stringData.erase(length, stringData.size() - length); str << L'"' << stringData << L'"'; if (memoryHandle) *memoryHandle = MemoryHandle::fromStdWString(stringData); return true; } } return false; } // Qt 4. wchar_t *memory; unsigned fullSize; unsigned size; if (!readQt5StringData(dV, qtInfo, true, position, std::min(length, ExtensionContext::instance().parameters().maxStringLength), &fullSize, &size, &memory)) return false; if (size) { str << L'"' << memory; if (std::min(fullSize, length) > size) str << L"..."; str << L'"'; } else { str << L"\"\""; } if (memoryHandle) *memoryHandle = new MemoryHandle(memory, size); else delete [] memory; return true; } static inline bool dumpQStringRef(const SymbolGroupValue &v, std::wostream &str, MemoryHandle **memoryHandle = 0) { const int position = v["m_position"].intValue(); const int size = v["m_size"].intValue(); if (position < 0 || size < 0) return false; const SymbolGroupValue string = v["m_string"]; if (!string || !dumpQString(string, str, memoryHandle, position, size)) return false; str << L" (" << position << ',' << size << L')'; return true; } /* Pad a memory offset to align with pointer size */ static inline unsigned padOffset(unsigned offset) { const unsigned ps = SymbolGroupValue::pointerSize(); return (offset % ps) ? (1 + offset / ps) * ps : offset; } /* Return the offset to be accounted for "QSharedData" to access * the first member of a QSharedData-derived class */ static unsigned qSharedDataSize(const SymbolGroupValueContext &ctx) { unsigned offset = 0; if (!offset) { // As of 4.X, a QAtomicInt, which will be padded to 8 on a 64bit system. const std::string qSharedData = QtInfo::get(ctx).prependQtCoreModule("QSharedData"); offset = SymbolGroupValue::sizeOf(qSharedData.c_str()); } return offset; } /* Return the size of a QString */ static unsigned qStringSize(const SymbolGroupValueContext &/*ctx*/) { // static const unsigned size = SymbolGroupValue::sizeOf(QtInfo::get(ctx).prependQtCoreModule("QString").c_str()); // FIXME: Workaround the issue that GetTypeSize returns strange values; static const unsigned size = SymbolGroupValue::pointerSize(); return size; } /* Return the size of a QList via QStringList. */ static unsigned qListSize(const SymbolGroupValueContext &ctx) { static const unsigned size = SymbolGroupValue::sizeOf(QtInfo::get(ctx).prependQtCoreModule("QStringList").c_str()); return size; } /* Return the size of a QByteArray */ static unsigned qByteArraySize(const SymbolGroupValueContext &/*ctx*/) { // static const unsigned size = SymbolGroupValue::sizeOf(QtInfo::get(ctx).prependQtCoreModule("QByteArray").c_str()); // FIXME: Workaround the issue that GetTypeSize returns strange values; static const unsigned size = SymbolGroupValue::pointerSize(); return size; } /* Return the size of a QAtomicInt */ static unsigned qAtomicIntSize(const SymbolGroupValueContext &ctx) { static const unsigned size = SymbolGroupValue::sizeOf(QtInfo::get(ctx).prependQtCoreModule("QAtomicInt").c_str()); return size; } // Dump a QByteArray static inline bool dumpQByteArray(const SymbolGroupValue &v, std::wostream &str, std::string *encoding, MemoryHandle **memoryHandle = 0) { const QtInfo &qtInfo = QtInfo::get(v.context()); const SymbolGroupValue dV = v["d"]; if (!dV) return false; // Qt 4. if (qtInfo.version < 5) { if (const SymbolGroupValue data = dV["data"]) { const DumpParameterRecodeResult check = checkCharArrayRecode(data); if (check.buffer) { str << quotedWStringFromCharData(check.buffer, check.size); delete [] check.buffer; } else { str << data.value(); } return true; } return false; } // Qt 5: Data start at offset past the 'd' of type QByteArrayData. if (encoding) *encoding = "latin1"; wchar_t oldFill = str.fill(wchar_t('0')); str << std::hex; char *memory; unsigned fullSize; unsigned size; const unsigned &maxStringSize = ExtensionContext::instance().parameters().maxStringLength; if (!readQt5StringData(dV, qtInfo, false, 0, maxStringSize, &fullSize, &size, &memory)) return false; if (size) { char *memEnd = memory + size; for (char *p = memory; p < memEnd; p++) { const unsigned char c = *p; str << std::setw(2) << c; } if (fullSize > size) str << L"2e2e2e"; // ... } if (memoryHandle) *memoryHandle = new MemoryHandle(reinterpret_cast(memory), size); else delete [] memory; str << std::dec; str.fill(oldFill); return true; } /* Below are some helpers for simple dumpers for some Qt classes accessing their * private classes without the debugger's symbolic information (applicable to non-exported * private classes such as QFileInfoPrivate, etc). This is done by dereferencing the * d-ptr and obtaining the address of the variable (considering some offsets depending on type) * and adding a symbol for that QString or QByteArray (basically using raw memory). */ enum QPrivateDumpMode // Enumeration determining the offsets to be taken into consideration { QPDM_None, QPDM_qVirtual, // For classes with virtual functions (QObject-based): Skip vtable for d-address QPDM_qSharedData, // Private class is based on QSharedData, non-padded type QPDM_qSharedDataPadded // Private class is based on QSharedData, padded type (class) }; // Determine the address of private class member by dereferencing the d-ptr and using offsets. static ULONG64 addressOfQPrivateMember(const SymbolGroupValue &v, QPrivateDumpMode mode, unsigned additionalOffset = 0) { std::string errorMessage; // 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, dAddress, &errorMessage); if (!dptr) return 0; // Get address of type to be dumped. ULONG64 dumpAddress = dptr + additionalOffset; if (mode == QPDM_qSharedData) { // Simple type following QSharedData dumpAddress += qSharedDataSize(v.context()); } else if (mode == QPDM_qSharedDataPadded) { dumpAddress += padOffset(qSharedDataSize(v.context())); } return dumpAddress; } // Convenience to dump a QString from the unexported private class of a Qt class. static bool dumpQStringFromQPrivateClass(const SymbolGroupValue &v, QPrivateDumpMode mode, unsigned additionalOffset, std::wostream &str) { std::string errorMessage; const ULONG64 stringAddress = addressOfQPrivateMember(v, mode, additionalOffset); if (!stringAddress) return false; std::string dumpType = QtInfo::get(v.context()).prependQtCoreModule("QString"); std::string symbolName = SymbolGroupValue::pointedToSymbolName(stringAddress , dumpType); if (SymbolGroupValue::verbose > 1) 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 && errorMessage.find("DEBUG_ANY_ID") != std::string::npos) { // HACK: // In some rare cases the AddSymbol can't create a node with a given module name, // but is able to add the symbol without any modulename. dumpType = QtInfo::get(v.context()).prependModuleAndNameSpace("QString", "", QtInfo::get(v.context()).nameSpace); symbolName = SymbolGroupValue::pointedToSymbolName(stringAddress , dumpType); stringNode = v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage); if (!stringNode) return false; } return dumpQString(SymbolGroupValue(stringNode, v.context()), str); } // Convenience to dump a QByteArray from the unexported private class of a Qt class. static bool dumpQByteArrayFromQPrivateClass(const SymbolGroupValue &v, QPrivateDumpMode mode, unsigned additionalOffset, std::wostream &str, std::string *encoding) { std::string errorMessage; const ULONG64 byteArrayAddress = addressOfQPrivateMember(v, mode, additionalOffset); if (!byteArrayAddress) return false; std::string dumpType = QtInfo::get(v.context()).prependQtCoreModule("QByteArray"); std::string symbolName = SymbolGroupValue::pointedToSymbolName(byteArrayAddress , dumpType); if (SymbolGroupValue::verbose > 1) DebugPrint() << "dumpQByteArrayFromQPrivateClass of " << v.name() << '/' << v.type() << " mode=" << mode << " offset=" << additionalOffset << " address=0x" << std::hex << byteArrayAddress << std::dec << " expr=" << symbolName; SymbolGroupNode *byteArrayNode = v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage); if (!byteArrayNode && errorMessage.find("DEBUG_ANY_ID") != std::string::npos) { // HACK: // In some rare cases the AddSymbol can't create a node with a given module name, // but is able to add the symbol without any modulename. dumpType = QtInfo::get(v.context()).prependModuleAndNameSpace("QByteArray", "", QtInfo::get(v.context()).nameSpace); symbolName = SymbolGroupValue::pointedToSymbolName(byteArrayAddress , dumpType); byteArrayNode = v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage); if (!byteArrayNode) return false; } return dumpQByteArray(SymbolGroupValue(byteArrayNode, v.context()), str, encoding); } /* Dump QFileInfo, for whose private class no debugging information is available. * Dump 2nd string past its QSharedData base class. */ static inline bool dumpQFileInfo(const SymbolGroupValue &v, std::wostream &str) { return dumpQStringFromQPrivateClass(v, QPDM_qSharedDataPadded, 0, str); } /* Dump QDir, for whose private class no debugging information is available. * Dump 1st string past its QSharedData base class. */ static bool inline dumpQDir(const SymbolGroupValue &v, std::wostream &str) { const unsigned offset = v.fieldOffset(QtInfo::get(v.context()).prependQtCoreModule("QDirPrivate").c_str(), "dirEntry.m_filePath"); return dumpQStringFromQPrivateClass(v, QPDM_None, offset, str); } /* Dump QRegExp, for whose private class no debugging information is available. * Dump 1st string past of its base class. */ static inline bool dumpQRegExp(const SymbolGroupValue &v, std::wostream &str) { return dumpQStringFromQPrivateClass(v, QPDM_qSharedDataPadded, 0, str); } static inline bool dumpQRegion(const SymbolGroupValue &v, std::wostream &str, void **specialInfo) { const QtInfo info = QtInfo::get(v.context()); SymbolGroupValue d = v["d"]["qt_rgn"]; std::ostringstream namestr; namestr << "(" << info.prependQtGuiModule("QRegionPrivate *") << ")(" << std::showbase << std::hex << d.pointerValue() << ')'; std::string tmp; SymbolGroupNode *qRegionPrivateNode = v.node()->symbolGroup()->addSymbol(v.module(), namestr.str(), std::string(), &tmp); if (!qRegionPrivateNode) return false; const SymbolGroupValue qRegionPrivateValue = SymbolGroupValue(qRegionPrivateNode, v.context()); if (!qRegionPrivateValue) return false; const int size = containerSize(KT_QVector, qRegionPrivateValue["rects"]); if (size == -1) return false; str << L'<' << size << L" items>"; if (specialInfo) *specialInfo = qRegionPrivateNode; return true; } /* Dump QFile, for whose private class no debugging information is available. * Dump the 1st string first past its QIODevicePrivate base class. */ static inline bool dumpQFile(const SymbolGroupValue &v, std::wostream &str) { // Get address of the file name string, obtain value by dumping a QString at address static unsigned qFileBasePrivateSize = 0; if (!qFileBasePrivateSize) { const QtInfo info = QtInfo::get(v.context()); const std::string qIoDevicePrivateType =info.prependQtCoreModule( info.version < 5 ? "QIODevicePrivate" : "QFileDevicePrivate"); qFileBasePrivateSize = padOffset(SymbolGroupValue::sizeOf(qIoDevicePrivateType.c_str())); } if (!qFileBasePrivateSize) return false; return dumpQStringFromQPrivateClass(v, QPDM_qVirtual, qFileBasePrivateSize, str); } static inline bool dumpQIPv6Address(const SymbolGroupValue &v, std::wostream &str, std::string *encoding) { unsigned char *p = SymbolGroupValue::readMemory( v.context().dataspaces, v["c"].address(), 16); if (!p || !encoding) return false; const wchar_t oldFill = str.fill('0'); str << std::hex; for (unsigned char *it = p; it < p + 16; ++it) { if ((p - it) % 2 == 0 && it != p) str << ':'; str << std::setw(2) << *it; } str << std::dec; str.fill(oldFill); *encoding = "ipv6addressandhexscopeid"; return true; } /* 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, std::string *encoding) { // Determine offset in private struct: qIPv6AddressType (array, unaligned) + uint32 + enum. const QtInfo info = QtInfo::get(v.context()); SymbolGroupValue d = v["d"]["d"]; std::ostringstream namestr; namestr << '(' << info.prependQtNetworkModule("QHostAddressPrivate *") << ")(" << std::showbase << std::hex << d.pointerValue() << ')'; std::string tmp; SymbolGroupNode *qHostAddressPrivateNode = v.node()->symbolGroup()->addSymbol(v.module(), namestr.str(), std::string(), &tmp); if (!qHostAddressPrivateNode) return false; const SymbolGroupValue qHostAddressPrivateValue = SymbolGroupValue(qHostAddressPrivateNode, v.context()); const bool parsed = readPODFromMemory(qHostAddressPrivateValue.context().dataspaces, qHostAddressPrivateValue["isParsed"].address(), sizeof(bool), false, &tmp); if (parsed) { const int protocol = qHostAddressPrivateValue["protocol"].intValue(-1); if (protocol < 0) { str << L"Uninitialized/Unknown protocol"; return true; } if (protocol == 1) { if (dumpQIPv6Address(qHostAddressPrivateValue["a6"], str, encoding)) return true; str << L"IPv6 protocol"; return true; } DebugPrint() << v.name().c_str() << ": " << parsed; const SymbolGroupValue a = qHostAddressPrivateValue["a"]; const unsigned int address = static_cast(SymbolGroupValue::readIntValue(v.context().dataspaces, a.address())); str << (address >> 24) << '.' << (address << 8 >> 24) << '.' << (address << 16 >> 24) << '.' << (address << 24 >> 24); return true; } unsigned offset = 0; if (info.version < 5) { 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; 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) { // Get address of the original-encoded byte array, obtain value by dumping at address if (QtInfo::get(v.context()).version < 5) { const ULONG offset = padOffset(qAtomicIntSize(v.context())) + 6 * qStringSize(v.context()) + qByteArraySize(v.context()); return dumpQByteArrayFromQPrivateClass(v, QPDM_None, offset, str, 0); } ULONG offset = qAtomicIntSize(v.context()) + SymbolGroupValue::intSize(); const unsigned stringSize = qStringSize(v.context()); str << L"Scheme: "; if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str)) return false; offset += stringSize; str << L" User: "; if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str)) return false; offset += stringSize; str << L" Password: "; if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str)) return false; offset += stringSize; str << L" Host: "; if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str)) return false; offset += stringSize; str << L" Path: "; if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str)) return false; offset += stringSize; str << L" Query: "; if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str)) return false; offset += stringSize; str << L" Fragment: "; if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str)) return false; return true; } // Dump QColor static bool dumpQColor(const SymbolGroupValue &v, std::wostream &str) { const SymbolGroupValue specV = v["cspec"]; if (!specV) return false; const int spec = specV.intValue(); if (spec == 0) { str << L""; return true; } if (spec < 1 || spec > 4) return false; const SymbolGroupValue arrayV = v["ct"]["array"]; if (!arrayV) return false; const int a0 = arrayV["0"].intValue(); const int a1 = arrayV["1"].intValue(); const int a2 = arrayV["2"].intValue(); const int a3 = arrayV["3"].intValue(); const int a4 = arrayV["4"].intValue(); if (a0 < 0 || a1 < 0 || a2 < 0 || a3 < 0 || a4 < 0) return false; switch (spec) { case 1: // Rgb str << L"RGB alpha=" << (a0 / 0x101) << L", red=" << (a1 / 0x101) << L", green=" << (a2 / 0x101) << ", blue=" << (a3 / 0x101); break; case 2: // Hsv str << L"HSV alpha=" << (a0 / 0x101) << L", hue=" << (a1 / 100) << L", sat=" << (a2 / 0x101) << ", value=" << (a3 / 0x101); break; case 3: // Cmyk str << L"CMYK alpha=" << (a0 / 0x101) << L", cyan=" << (a1 / 100) << L", magenta=" << (a2 / 0x101) << ", yellow=" << (a3 / 0x101) << ", black=" << (a4 / 0x101); break; case 4: // Hsl str << L"HSL alpha=" << (a0 / 0x101) << L", hue=" << (a1 / 100) << L", sat=" << (a2 / 0x101) << ", lightness=" << (a3 / 0x101); break; } return true; } // Dump Qt's core types static inline bool dumpQBasicAtomicInt(const SymbolGroupValue &v, std::wostream &str) { if (const SymbolGroupValue iValue = v["_q_value"]) { str << iValue.value(); return true; } return false; } static inline bool dumpQAtomicInt(const SymbolGroupValue &v, std::wostream &str) { if (const SymbolGroupValue base = v[unsigned(0)]) return dumpQBasicAtomicInt(base, str); return false; } static bool dumpQChar(const SymbolGroupValue &v, std::wostream &str) { if (SymbolGroupValue cValue = v["ucs"]) { const int utf16 = cValue.intValue(); if (utf16 >= 0) { // Print code = character, // exclude control characters and Pair indicator if (utf16 >= 32 && (utf16 < 0xD800 || utf16 > 0xDBFF)) str << '\'' << wchar_t(utf16) << "' "; str << '(' << utf16 << ')'; } return true; } return false; } static inline bool dumpQFlags(const SymbolGroupValue &v, std::wostream &str) { if (SymbolGroupValue iV = v["i"]) { const int i = iV.intValue(); if (i >= 0) { str << i; return true; } } return false; } static bool dumpQDate(const SymbolGroupValue &v, std::wostream &str, std::string *encoding) { if (const SymbolGroupValue julianDayV = v["jd"]) { if (julianDayV.intValue() > 0) { str << julianDayV.intValue(); if (encoding) *encoding = "juliandate"; } else { str << L"(invalid)"; } return true; } return false; } static bool dumpQTime(const SymbolGroupValue &v, std::wostream &str, std::string *encoding) { if (const SymbolGroupValue milliSecsV = v["mds"]) { const int milliSecs = milliSecsV.intValue(); str << milliSecs; if (encoding) *encoding = "millisecondssincemidnight"; return true; } return false; } static bool dumpQTimeZone(const SymbolGroupValue &v, std::wostream &str, std::string *encoding) { if (!dumpQByteArrayFromQPrivateClass(v, QPDM_qSharedDataPadded, SymbolGroupValue::pointerSize(), str, encoding)) str << L"(null)"; return true; } // Convenience to dump a QTimeZone from the unexported private class of a Qt class. static bool dumpQTimeZoneFromQPrivateClass(const SymbolGroupValue &v, QPrivateDumpMode mode, unsigned additionalOffset, std::wostream &str, std::string *encoding) { std::string errorMessage; const ULONG64 timeZoneAddress = addressOfQPrivateMember(v, mode, additionalOffset); if (!timeZoneAddress) return false; std::string dumpType = QtInfo::get(v.context()).prependQtCoreModule("QTimeZone"); std::string symbolName = SymbolGroupValue::pointedToSymbolName(timeZoneAddress , dumpType); if (SymbolGroupValue::verbose > 1) DebugPrint() << "dumpQTimeZoneFromQPrivateClass of " << v.name() << '/' << v.type() << " mode=" << mode << " offset=" << additionalOffset << " address=0x" << std::hex << timeZoneAddress << std::dec << " expr=" << symbolName; SymbolGroupNode *timeZoneNode = v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage); if (!timeZoneNode && errorMessage.find("DEBUG_ANY_ID") != std::string::npos) { // HACK: // In some rare cases the AddSymbol can't create a node with a given module name, // but is able to add the symbol without any modulename. dumpType = QtInfo::get(v.context()).prependModuleAndNameSpace("QTimeZone", "", QtInfo::get(v.context()).nameSpace); symbolName = SymbolGroupValue::pointedToSymbolName(timeZoneAddress , dumpType); timeZoneNode = v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage); if (!timeZoneNode) return false; } return dumpQTimeZone(SymbolGroupValue(timeZoneNode, v.context()), str, encoding); } // QDateTime has an unexported private class. Obtain date and time // from memory. static bool dumpQDateTime(const SymbolGroupValue &v, std::wostream &str, std::string *encoding) { // QDate is 64bit starting from Qt 5 which is always aligned 64bit. if (QtInfo::get(v.context()).version == 5) { // the dumper on the creator side expects msecs/spec/offset/tz/status/type info version const char separator = '/'; LONG64 msecs = 0; int spec = 0; int offset = 0; std::wstring timeZoneString; int status = 0; int tiVersion = QtInfo::qtTypeInfoVersion(v.context()); if (tiVersion > 10) { const ULONG64 address = SymbolGroupValue::isPointerType(v.type()) ? v.pointerValue() : v.address(); const ULONG64 data = SymbolGroupValue::readUnsignedValue( v.context().dataspaces, address, 8, 0); status = data & 0xFF; ULONG64 timeZone = 0; if (status & 0x01) { msecs = data >> 8; spec = (status & 0x30) >> 4; } else { ULONG64 addr = SymbolGroupValue::readPointerValue(v.context().dataspaces, address); msecs = SymbolGroupValue::readSignedValue(v.context().dataspaces, addr, 8, 0); addr += 8 /*int64*/; status = SymbolGroupValue::readIntValue(v.context().dataspaces, addr); addr += SymbolGroupValue::intSize(); offset = SymbolGroupValue::readIntValue(v.context().dataspaces, addr); addr += 2 * SymbolGroupValue::intSize(); timeZone = SymbolGroupValue::readPointerValue(v.context().dataspaces, addr); } timeZoneString = std::to_wstring(timeZone); } else { const ULONG64 msecsAddr = addressOfQPrivateMember(v, QPDM_None, 0); if (!msecsAddr) return false; int addrOffset = 8 /*QSharedData + padded*/; msecs = SymbolGroupValue::readSignedValue( v.context().dataspaces, msecsAddr + addrOffset, 8, 0); addrOffset += 8 /*int64*/; spec = SymbolGroupValue::readIntValue(v.context().dataspaces, msecsAddr + addrOffset); addrOffset += SymbolGroupValue::sizeOf("Qt::TimeSpec"); offset = SymbolGroupValue::readIntValue(v.context().dataspaces, msecsAddr + addrOffset); addrOffset += SymbolGroupValue::intSize(); std::wostringstream timeZoneStream; dumpQTimeZoneFromQPrivateClass(v, QPDM_None, addrOffset, timeZoneStream, 0); timeZoneString = timeZoneStream.str(); addrOffset += SymbolGroupValue::sizeOf("QTimeZone"); status = SymbolGroupValue::readIntValue(v.context().dataspaces, msecsAddr + addrOffset); } enum StatusFlag { ValidDate = 0x04, ValidTime = 0x08, ValidDateTime = 0x10 }; if (!(status & ValidDateTime || ((status & ValidDate) && (status & ValidTime)))) { str << L"(invalid)"; return true; } str << msecs << separator << spec << separator << offset << separator << timeZoneString << separator << status << separator << tiVersion; if (encoding) *encoding = "datetimeinternal"; return true; } const ULONG64 dateAddr = addressOfQPrivateMember(v, QPDM_qSharedData, 0); if (!dateAddr) return false; const int date = SymbolGroupValue::readIntValue(v.context().dataspaces, dateAddr, SymbolGroupValue::intSize(), 0); if (!date) { str << L""; return true; } const ULONG64 timeAddr = dateAddr + SymbolGroupValue::intSize(); const int time = SymbolGroupValue::readIntValue(v.context().dataspaces, timeAddr, SymbolGroupValue::intSize(), 0); str << date << '/' << time; if (encoding) *encoding = "juliandateandmillisecondssincemidnight"; return true; } static bool dumpQPixmap(const SymbolGroupValue &v, std::wostream &str) { const SymbolGroupValue pixmapSharedData = v["data"]["d"]; if (!pixmapSharedData.isValid()) return false; ULONG64 addr = pixmapSharedData.pointerValue(); if (addr) { const unsigned int width = SymbolGroupValue::readIntValue(v.context().dataspaces, addr += SymbolGroupValue::pointerSize(), SymbolGroupValue::intSize(), 0); const unsigned int height = SymbolGroupValue::readIntValue(v.context().dataspaces, addr += SymbolGroupValue::intSize(), SymbolGroupValue::intSize(), 0); const unsigned int depth = SymbolGroupValue::readIntValue(v.context().dataspaces, addr += SymbolGroupValue::intSize(), SymbolGroupValue::intSize(), 0); if (width && height) { str << width << L'x' << height << L", depth: " << depth; return true; } } str << L""; return true; } static bool dumpQImage(const SymbolGroupValue &v, std::wostream &str, MemoryHandle **memoryHandle) { struct CreatorImageHeader { // Header for image display as edit format, followed by data. int width; int height; int format; }; const QtInfo &qtInfo(QtInfo::get(v.context())); // Fetch data of unexported private class const ULONG64 address = v["d"].pointerValue(); if (!address) { str << L""; return true; } const std::string qImageDataType = qtInfo.prependQtGuiModule("QImageData"); const unsigned long size = SymbolGroupValue::sizeOf(qImageDataType.c_str()); if (!size) return false; unsigned char *qImageData = SymbolGroupValue::readMemory(v.context().dataspaces, address, size); if (!qImageData) return false; // read size data unsigned char *ptr = qImageData + qAtomicIntSize(v.context()); CreatorImageHeader header; header.width = *(reinterpret_cast(ptr)); ptr += SymbolGroupValue::intSize(); header.height = *(reinterpret_cast(ptr)); ptr += SymbolGroupValue::intSize(); const int depth = *(reinterpret_cast(ptr)); ptr += SymbolGroupValue::intSize(); const int nbytes = *(reinterpret_cast(ptr)); const unsigned dataOffset = SymbolGroupValue::fieldOffset(qImageDataType.c_str(), "data"); // Qt 4 has a Qt 3 support pointer member between 'data' and 'format'. const unsigned formatOffset = SymbolGroupValue::fieldOffset(qImageDataType.c_str(), "format"); if (!dataOffset || !formatOffset) return false; ptr = qImageData + dataOffset; // read data pointer ULONG64 data = 0; memcpy(&data, ptr, SymbolGroupValue::pointerSize()); // read format ptr = qImageData + formatOffset; header.format = *(reinterpret_cast(ptr)); if (header.width < 0 || header.height < 0 || header.format < 0 || header.format > 255 || nbytes < 0 || depth < 0) { return false; } str << header.width << L'x' << header.height << L", depth: " << depth << L", format: " << header.format << L", " << nbytes << L" bytes"; delete [] qImageData; // Create Creator Image data for display if reasonable size if (memoryHandle && data && nbytes > 0 && nbytes < 205824) { if (unsigned char *imageData = SymbolGroupValue::readMemory(v.context().dataspaces, data, nbytes)) { unsigned char *creatorImageData = new unsigned char[sizeof(CreatorImageHeader) + nbytes]; memcpy(creatorImageData, &header, sizeof(CreatorImageHeader)); memcpy(creatorImageData + sizeof(CreatorImageHeader), imageData, nbytes); delete [] imageData; *memoryHandle = new MemoryHandle(creatorImageData, sizeof(CreatorImageHeader) + nbytes); // cppcheck: don't delete[] creatorImageData here, it's taken over by MemoryHandle } } return true; } // Dump a rectangle in X11 syntax template inline void dumpRect(std::wostream &str, T x, T y, T width, T height) { str << width << 'x' << height; if (x >= 0) str << '+'; str << x; if (y >= 0) str << '+'; str << y; } // Dump Qt's simple geometrical types static inline bool dumpQSize_F(const SymbolGroupValue &v, std::wostream &str) { str << '(' << v["wd"].value() << ", " << v["ht"].value() << ')'; return true; } static inline bool dumpQPoint_F(const SymbolGroupValue &v, std::wostream &str) { str << '(' << v["xp"].value() << ", " << v["yp"].value() << ')'; return true; } static inline bool dumpQLine_F(const SymbolGroupValue &v, std::wostream &str) { const SymbolGroupValue p1 = v["pt1"]; const SymbolGroupValue p2 = v["pt2"]; if (p1 && p2) { str << '(' << p1["xp"].value() << ", " << p1["yp"].value() << ") (" << p2["xp"].value() << ", " << p2["yp"].value() << ')'; return true; } return false; } static inline bool dumpQRect(const SymbolGroupValue &v, std::wostream &str) { const int x1 = v["x1"].intValue(); const int y1 = v["y1"].intValue(); const int x2 = v["x2"].intValue(); const int y2 = v["y2"].intValue(); dumpRect(str, x1, y1, (x2 - x1 + 1), (y2 - y1 + 1)); return true; } static inline bool dumpQRectF(const SymbolGroupValue &v, std::wostream &str) { dumpRect(str, v["xp"].floatValue(), v["yp"].floatValue(), v["w"].floatValue(), v["h"].floatValue()); return true; } /* Return a SymbolGroupValue containing the private class of * a type (multiple) derived from QObject and something else * (QWidget: QObject,QPaintDevice or QWindow: QObject,QSurface). * We get differing behaviour for pointers and values on stack. * For 'QWidget *', the base class QObject usually can be accessed * by name (past the vtable). When browsing class hierarchies (stack), * typically only the uninteresting QPaintDevice is seen. */ SymbolGroupValue qobjectDerivedPrivate(const SymbolGroupValue &v, const std::string &qwPrivateType, const QtInfo &qtInfo) { if (const SymbolGroupValue base = v[SymbolGroupValue::stripModuleFromType(qtInfo.qObjectType).c_str()]) if (const SymbolGroupValue qwPrivate = base["d_ptr"]["d"].pointerTypeCast(qwPrivateType.c_str())) return qwPrivate; if (!SymbolGroupValue::isPointerType(v.type())) return SymbolGroupValue(); // Class hierarchy: Using brute force, add new symbol based on that // QScopedPointer is basically a 'X *' (first member). std::string errorMessage; std::ostringstream str; str << '(' << qwPrivateType << "*)(" << std::showbase << std::hex << v.address() << ')'; const std::string name = str.str(); SymbolGroupNode *qwPrivateNode = v.node()->symbolGroup()->addSymbol(v.module(), name, std::string(), &errorMessage); return SymbolGroupValue(qwPrivateNode, v.context()); } static bool dumpQObjectName(const SymbolGroupValue &qoPrivate, std::wostream &str) { // Qt 4: plain member. if (QtInfo::get(qoPrivate.context()).version < 5) { if (const SymbolGroupValue oName = qoPrivate["objectName"]) return dumpQString(oName, str); } // Qt 5: member of allocated extraData. if (const SymbolGroupValue extraData = qoPrivate["extraData"]) if (extraData.pointerValue()) if (const SymbolGroupValue oName = extraData["objectName"]) return dumpQString(oName, str); return false; } // Dump the object name static inline bool dumpQWidget(const SymbolGroupValue &v, std::wostream &str, void **specialInfoIn = 0) { const QtInfo &qtInfo = QtInfo::get(v.context()); const SymbolGroupValue qwPrivate = qobjectDerivedPrivate(v, qtInfo.qWidgetPrivateType, qtInfo); // QWidgetPrivate inherits QObjectPrivate if (!qwPrivate || !dumpQObjectName(qwPrivate[unsigned(0)], str)) return false; if (specialInfoIn) *specialInfoIn = qwPrivate.node(); return true; } // Dump the object name static inline bool dumpQObject(const SymbolGroupValue &v, std::wostream &str, void **specialInfoIn = 0) { const std::string &qoPrivateType = QtInfo::get(v.context()).qObjectPrivateType; const SymbolGroupValue qoPrivate = v["d_ptr"]["d"].pointerTypeCast(qoPrivateType.c_str()); if (!qoPrivate || !dumpQObjectName(qoPrivate, str)) return false; if (specialInfoIn) *specialInfoIn = qoPrivate.node(); return true; } // Dump the object name static inline bool dumpQWindow(const SymbolGroupValue &v, std::wostream &str, void **specialInfoIn = 0) { const QtInfo &qtInfo = QtInfo::get(v.context()); const SymbolGroupValue qwPrivate = qobjectDerivedPrivate(v, qtInfo.qWindowPrivateType, qtInfo); // QWindowPrivate inherits QObjectPrivate if (!qwPrivate || !dumpQObjectName(qwPrivate[unsigned(0)], str)) return false; if (specialInfoIn) *specialInfoIn = qwPrivate.node(); return true; } //Dump a QTextCursor static inline bool dumpQTextCursor(const SymbolGroupValue &v, std::wostream &str) { const unsigned offset = SymbolGroupValue::pointerSize() + SymbolGroupValue::sizeOf("double"); const ULONG64 posAddr = addressOfQPrivateMember(v, QPDM_qSharedDataPadded, offset); if (!posAddr) return false; const int position = SymbolGroupValue::readIntValue(v.context().dataspaces, posAddr); str << position; return true; } // Dump a std::string. static bool dumpStd_W_String(const SymbolGroupValue &v, int type, std::wostream &str, MemoryHandle **memoryHandle = 0) { // Find 'bx'. MSVC 2012 has 2 base classes, MSVC 2010 1, // and MSVC2008 none const SymbolGroupValue bx = SymbolGroupValue::findMember(v, "_Bx"); const int reserved = bx.parent()["_Myres"].intValue(); int size = bx.parent()["_Mysize"].intValue(); if (!bx || reserved < 0 || size < 0) return false; const bool truncated = unsigned(size) > ExtensionContext::instance().parameters().maxStringLength; if (truncated) size = ExtensionContext::instance().parameters().maxStringLength; // 'Buf' array for small strings, else pointer 'Ptr'. const int bufSize = type == KT_StdString ? 16 : 8; // see basic_string. const unsigned long memSize = type == KT_StdString ? size : 2 * size; const ULONG64 address = reserved >= bufSize ? bx["_Ptr"].pointerValue() : bx["_Buf"].address(); if (!address) return false; unsigned char *memory = SymbolGroupValue::readMemory(v.context().dataspaces, address, memSize); if (!memory) return false; str << (type == KT_StdString ? quotedWStringFromCharData(memory, memSize, truncated) : quotedWStringFromWCharData(memory, memSize, truncated)); if (memoryHandle) *memoryHandle = new MemoryHandle(memory, memSize); else delete [] memory; return true; } // Dump a std::complex. static bool dumpStd_Complex(const SymbolGroupValue &v, std::wostream &str) { if (const SymbolGroupValue &valArray = v[0u][0u]["_Val"]) { if (const SymbolGroupValue &val0 = valArray["0"]) { str << L'(' << val0.value(); if (const SymbolGroupValue &val1 = valArray["1"]) { str << L", " << val1.value() << L')'; return true; } } } return false; } // QVariant employs a template for storage where anything bigger than the data union // is pointed to by data.shared.ptr, else it is put into the data struct (pointer size) // itself (notably Qt types consisting of a d-ptr only). // The layout can vary between 32 /64 bit for some types: QPoint/QSize (of 2 ints) is bigger // as a pointer only on 32 bit. static inline SymbolGroupValue qVariantCast(const SymbolGroupValue &variantData, const char *type) { const ULONG typeSize = SymbolGroupValue::sizeOf(type); const std::string ptrType = std::string(type) + " *"; if (typeSize > variantData.size()) return variantData["shared"]["ptr"].pointerTypeCast(ptrType.c_str()); return variantData.typeCast(ptrType.c_str()); } // Qualify a local container template of Qt Types for QVariant // as 'QList' of 'QVariant' -> 'localModule!qtnamespace::QList *' static inline std::string variantContainerType(const std::string &containerType, const std::string &innerType1, const std::string &innerType2 /* = "" */, const QtInfo &qtInfo, const SymbolGroupValue &contextHelper) { std::string rc = QtInfo::prependModuleAndNameSpace(containerType, contextHelper.module(), qtInfo.nameSpace); rc.push_back('<'); rc += QtInfo::prependModuleAndNameSpace(innerType1, std::string(), qtInfo.nameSpace); if (!innerType2.empty()) { rc.push_back(','); rc += QtInfo::prependModuleAndNameSpace(innerType2, std::string(), qtInfo.nameSpace); } rc += "> *"; return rc; } static bool dumpQVariant(const SymbolGroupValue &v, std::wostream &str, std::string *encoding, void **specialInfoIn = 0) { const QtInfo &qtInfo = QtInfo::get(v.context()); const SymbolGroupValue dV = v["d"]; if (!dV) return false; const SymbolGroupValue typeV = dV["type"]; const SymbolGroupValue dataV = dV["data"]; if (!typeV || !dataV) return false; const int typeId = typeV.intValue(); if (typeId <= 0) { str << L""; return true; } switch (typeId) { case 1: // Bool str << L"(bool) " << dataV["b"].value(); break; case 2: // Int str << L"(int) " << dataV["i"].value(); break; case 3: // UInt str << L"(unsigned) " << dataV["u"].value(); break; case 4: // LongLong str << L"(long long) " << dataV["ll"].value(); break; case 5: // LongLong str << L"(unsigned long long) " << dataV["ull"].value(); break; case 6: // Double str << L"(double) " << dataV["d"].value(); break; case 7: // Char str << L"(char) " << dataV["c"].value(); break; case 8: { str << L"(QVariantMap) "; const std::string vmType = variantContainerType("QMap", "QString", "QVariant", qtInfo, dataV); if (const SymbolGroupValue mv = dataV.typeCast(vmType.c_str())) { SymbolGroupNode *mapNode = mv.node(); std::wstring value; std::string tmp; if (dumpSimpleType(mapNode, dataV.context(), &value, &tmp) == SymbolGroupNode::SimpleDumperOk) { str << value; if (specialInfoIn) *specialInfoIn = mapNode; } } } break; case 9: { // QVariantList str << L"(QVariantList) "; const std::string vLType = variantContainerType("QList", "QVariant", std::string(), qtInfo, dataV); if (const SymbolGroupValue vl = dataV.typeCast(vLType.c_str())) { SymbolGroupNode *vListNode = vl.node(); std::wstring value; std::string tmp; if (dumpSimpleType(vListNode, dataV.context(), &value, &tmp) == SymbolGroupNode::SimpleDumperOk) { str << value; if (specialInfoIn) *specialInfoIn = vListNode; } } } break; case 10: // String str << L"(QString) "; if (const SymbolGroupValue sv = dataV.typeCast(qtInfo.prependQtCoreModule("QString *").c_str())) { if (!dumpQString(sv, str)) { // HACK: // In some rare cases the AddSymbol can't create a node with a given module name, // but is able to add the symbol without any modulename. if (const SymbolGroupValue svc = dataV.typeCast("QString *")) dumpQString(svc, str); } } break; case 11: //StringList: Dump container size str << L"(QStringList) "; if (const SymbolGroupValue sl = dataV.typeCast(qtInfo.prependQtCoreModule("QStringList *").c_str())) { SymbolGroupNode *listNode = sl.node(); std::wstring value; std::string tmp; if (dumpSimpleType(listNode, dataV.context(), &value, &tmp) == SymbolGroupNode::SimpleDumperOk) { str << value; if (specialInfoIn) *specialInfoIn = listNode; } } break; case 12: //ByteArray str << L"(QByteArray) "; if (const SymbolGroupValue sv = dataV.typeCast(qtInfo.prependQtCoreModule("QByteArray *").c_str())) dumpQByteArray(sv, str, encoding); break; case 13: // BitArray str << L"(QBitArray)"; break; case 14: // Date: Do not qualify - fails non-deterministically with QtCored4!QDate str << L"(QDate) "; if (const SymbolGroupValue sv = dataV.typeCast("QDate *")) dumpQDate(sv, str, encoding); break; case 15: // Time: Do not qualify - fails non-deterministically with QtCored4!QTime str << L"(QTime) "; if (const SymbolGroupValue sv = dataV.typeCast("QTime *")) dumpQTime(sv, str, encoding); break; case 16: // DateTime str << L"(QDateTime)"; break; case 17: // Url str << L"(QUrl)"; break; case 18: // Locale str << L"(QLocale)"; break; case 19: // Rect: str << L"(QRect) "; if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QRect *").c_str())) dumpQRect(sv, str); break; case 20: // RectF str << L"(QRectF) "; if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QRectF *").c_str())) dumpQRectF(sv, str); break; case 21: // Size // Anything bigger than the data union is a pointer, else the data union is used str << L"(QSize) "; if (const SymbolGroupValue sv = qVariantCast(dataV, qtInfo.prependQtCoreModule("QSize").c_str())) dumpQSize_F(sv, str); break; case 22: // SizeF str << L"(QSizeF) "; if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QSizeF *").c_str())) dumpQSize_F(sv, str); break; case 23: // Line str << L"(QLine) "; if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QLine *").c_str())) dumpQLine_F(sv, str); break; case 24: // LineF str << L"(QLineF) "; if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QLineF *").c_str())) dumpQLine_F(sv, str); break; case 25: // Point str << L"(QPoint) "; if (const SymbolGroupValue sv = qVariantCast(dataV, qtInfo.prependQtCoreModule("QPoint").c_str())) dumpQPoint_F(sv, str); break; case 26: // PointF str << L"(QPointF) "; if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QPointF *").c_str())) dumpQPoint_F(sv, str); break; case 65: // QPixmap str << L"(QPixmap) "; if (const SymbolGroupValue sv = qVariantCast(dataV, qtInfo.prependQtGuiModule("QPixmap").c_str())) dumpQPixmap(sv, str); break; default: str << L"Type " << typeId; break; } return true; } static inline bool dumpQSharedPointer(const SymbolGroupValue &v, std::wostream &str, std::string *encoding, void **specialInfoIn = 0) { const SymbolGroupValue externalRefCountV = v[unsigned(0)]; const QtInfo qtInfo = QtInfo::get(v.context()); if (qtInfo.version < 5) { if (!externalRefCountV) return false; const SymbolGroupValue dV = externalRefCountV["d"]; if (!dV) return false; // Get value element from base and store in special info. const SymbolGroupValue valueV = externalRefCountV[unsigned(0)]["value"]; if (!valueV) return false; // Format references. const int strongRef = dV["strongref"]["_q_value"].intValue(); const int weakRef = dV["weakref"]["_q_value"].intValue(); if (strongRef < 0 || weakRef < 0) return false; str << L"References: " << strongRef << '/' << weakRef; if (specialInfoIn) *specialInfoIn = valueV.node(); return true; } else { // Qt 5 SymbolGroupValue value = v["value"]; if (value.pointerValue(0) == 0) { str << L"(null)"; return true; } if (knownType(value.type(), KnownTypeAutoStripPointer | KnownTypeHasClassPrefix) & KT_HasSimpleDumper) { str << value.node()->simpleDumpValue(v.context(), encoding); return true; } return false; } } // Dump builtin simple types using SymbolGroupValue expressions. unsigned dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx, std::wstring *s, std::string *encoding, int *knownTypeIn /* = 0 */, int *containerSizeIn /* = 0 */, void **specialInfoIn /* = 0 */, MemoryHandle **memoryHandleIn /* = 0 */) { QTC_TRACE_IN if (containerSizeIn) *containerSizeIn = -1; if (specialInfoIn) *specialInfoIn = 0; // Check for class types and strip pointer types (references appear as pointers as well) s->clear(); const KnownType kt = knownType(n->type(), KnownTypeHasClassPrefix|KnownTypeAutoStripPointer); if (knownTypeIn) *knownTypeIn = kt; if (kt == KT_Unknown || !(kt & KT_HasSimpleDumper)) { if (SymbolGroupValue::verbose > 1) DebugPrint() << "dumpSimpleType N/A " << n->name() << '/' << n->type(); QTC_TRACE_OUT return SymbolGroupNode::SimpleDumperNotApplicable; } std::wostringstream str; // Prefix by pointer value const SymbolGroupValue v(n, ctx); if (!v) // Value as such has memory read error? return SymbolGroupNode::SimpleDumperFailed; unsigned rc = SymbolGroupNode::SimpleDumperNotApplicable; // Simple dump of size for containers if (kt & KT_ContainerType) { const int size = containerSize(kt, v); if (SymbolGroupValue::verbose > 1) DebugPrint() << "dumpSimpleType Container " << n->name() << '/' << n->type() << " size=" << size; if (containerSizeIn) *containerSizeIn = size; if (size >= 0) { str << L'<' << size << L" items>"; rc = SymbolGroupNode::SimpleDumperOk; } else { rc = SymbolGroupNode::SimpleDumperFailed; } } else { switch (kt) { case KT_QChar: rc = dumpQChar(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QByteArray: rc = dumpQByteArray(v, str, encoding, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QFileInfo: rc = dumpQFileInfo(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QFile: rc = dumpQFile(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QDir: rc = dumpQDir(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QRegExp: rc = dumpQRegExp(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QRegion: rc = dumpQRegion(v, str, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QUrl: rc = dumpQUrl(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QHostAddress: rc = dumpQHostAddress(v, str, encoding) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QIPv6Address: rc = dumpQIPv6Address(v, str, encoding) ? 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, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QStringRef: rc = dumpQStringRef(v, str, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QColor: rc = dumpQColor(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QFlags: rc = dumpQFlags(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QDate: rc = dumpQDate(v, str, encoding) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QTime: rc = dumpQTime(v, str, encoding) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QDateTime: rc = dumpQDateTime(v, str, encoding) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QTimeZone: rc = dumpQTimeZone(v, str, encoding) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QPoint: case KT_QPointF: rc = dumpQPoint_F(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QSize: case KT_QSizeF: rc = dumpQSize_F(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QLine: case KT_QLineF: rc = dumpQLine_F(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QPixmap: rc = dumpQPixmap(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QImage: rc = dumpQImage(v, str, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QRect: rc = dumpQRect(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QRectF: rc = dumpQRectF(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QVariant: rc = dumpQVariant(v, str, encoding, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QAtomicInt: rc = dumpQAtomicInt(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QBasicAtomicInt: rc = dumpQBasicAtomicInt(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QObject: rc = dumpQObject(v, str, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QWidget: rc = dumpQWidget(v, str, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QWindow: rc = dumpQWindow(v, str, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QSharedPointer: case KT_QWeakPointer: rc = dumpQSharedPointer(v, str, encoding, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_StdString: case KT_StdWString: rc = dumpStd_W_String(v, kt, str, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_StdComplex: rc = dumpStd_Complex(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; case KT_QTextCursor: rc = dumpQTextCursor(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed; break; default: break; } } if (rc != SymbolGroupNode::SimpleDumperFailed && SymbolGroupValue::isPointerType(v.type()) && encoding->empty()) str << L" @" << std::showbase << std::hex << v.pointerValue() << std::dec << std::noshowbase; if (rc == SymbolGroupNode::SimpleDumperOk) *s = str.str(); QTC_TRACE_OUT if (SymbolGroupValue::verbose > 1) { DebugPrint dp; dp << "dumpSimpleType " << n->name() << '/' << n->type() << " knowntype= " << kt << " ["; formatKnownTypeFlags(dp, kt); dp << "] returns " << rc; } return rc; } static inline void formatEditValue(const std::string &displayFormat, const MemoryHandle *mh, std::ostream &str) { str << "editformat=\"" << displayFormat << "\",editvalue=\"" << mh->toHex() << "\","; } void dumpEditValue(const SymbolGroupNode *n, const SymbolGroupValueContext &, const std::string &desiredFormat, std::ostream &str) { if (SymbolGroupValue::verbose) DebugPrint() << __FUNCTION__ << ' ' << n->name() << '/' << desiredFormat; auto separatorPos = desiredFormat.find(':'); if (separatorPos == std::string::npos) return; if (desiredFormat.substr(separatorPos) != "separate") return; if (const MemoryHandle *mh = n->memory()) formatEditValue(desiredFormat, mh, str); } // Dump of QByteArray: Display as an array of unsigned chars. static inline std::vector complexDumpQByteArray(SymbolGroupNode *n, const SymbolGroupValueContext &ctx) { std::vector rc; const SymbolGroupValue baV(n, ctx); const SymbolGroupValue dV = baV["d"]; if (!dV) return rc; // Determine memory area. unsigned size = 0; ULONG64 address = 0; const QtStringAddressData data = readQtStringAddressData(dV, QtInfo::get(ctx)); size = data.size; address = data.address; if (size <= 0 || !address) return rc; if (size > 200) size = 200; rc.reserve(size); const std::string charType = "char"; std::string errorMessage; SymbolGroup *sg = n->symbolGroup(); for (int i = 0; i < (int)size; ++i, ++address) { SymbolGroupNode *en = sg->addSymbol(std::string(), SymbolGroupValue::pointedToSymbolName(address, charType), std::string(), &errorMessage); if (!en) { rc.clear(); return rc; } rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, en)); } return rc; } // Assignment helpers static inline std::string msgAssignStringFailed(const std::string &value, int errorCode) { std::ostringstream estr; estr << "Unable to assign a string of " << value.size() << " bytes: Error " << errorCode; return estr.str(); } /* QString assign helper: If QString instance has sufficiently allocated, * memory, write the data. Else invoke 'QString::resize' and * recurse (since 'd' might become invalid). This works for QString with UTF16 * data and for QByteArray with ASCII data due to the similar member * names and both using a terminating '\0' w_char/byte. */ template static int assignQStringI(SymbolGroupNode *n, const char *className, const string &data, const SymbolGroupValueContext &ctx, bool doAlloc = true) { const SymbolGroupValue v(n, ctx); SymbolGroupValue d = v["d"]; if (!d) return 1; const QtInfo &qtInfo = QtInfo::get(ctx); // Check the size, re-allocate if required. const QtStringAddressData addressData = readQtStringAddressData(d, qtInfo); if (!addressData.address) return 9; const bool needRealloc = addressData.allocated < data.size(); if (needRealloc) { if (!doAlloc) // Calling re-alloc failed somehow. return 3; std::ostringstream callStr; const std::string funcName = qtInfo.prependQtCoreModule(std::string(className) + "::resize"); callStr << funcName << '(' << std::hex << std::showbase << v.address() << ',' << data.size() << ')'; std::wstring wOutput; std::string errorMessage; return ExtensionContext::instance().call(callStr.str(), 0, &wOutput, &errorMessage) ? assignQStringI(n, className, data, ctx, false) : 5; } // Write data. if (!SymbolGroupValue::writeMemory(v.context().dataspaces, addressData.address, (const unsigned char *)(data.c_str()), ULONG(data.empty() ? 0 : sizeof(data.front()) * data.size()))) return 11; // Correct size unless we re-allocated if (!needRealloc) { const std::string &arrayData = qtInfo.prependModuleAndNameSpace("QArrayData", std::string(), qtInfo.nameSpace); const SymbolGroupValue dV = qtInfo.version < 5 ? d : d[arrayData.c_str()]; if (!dV) return 14; const SymbolGroupValue size = dV["size"]; if (!size) return 16; if (!size.node()->assign(toString(data.size()))) return 17; } return 0; } // QString assignment static inline bool assignQString(SymbolGroupNode *n, const std::string &value, const SymbolGroupValueContext &ctx, std::string *errorMessage) { const int errorCode = assignQStringI(n, "QString", utf8ToUtf16(value), ctx); if (errorCode) { *errorMessage = msgAssignStringFailed(value, errorCode); return false; } return true; } // QByteArray assignment static inline bool assignQByteArray(SymbolGroupNode *n, const std::string &value, const SymbolGroupValueContext &ctx, std::string *errorMessage) { const int errorCode = assignQStringI(n, "QByteArray", value, ctx); if (errorCode) { *errorMessage = msgAssignStringFailed(value, errorCode); return false; } return true; } // Helper to assign character data to std::string or std::wstring. template static inline int assignStdStringI(SymbolGroupNode *n, int type, const string &data, const SymbolGroupValueContext &ctx) { /* We do not reallocate and just write to the allocated buffer * (internal small buffer or _Ptr depending on reserved) provided sufficient * memory is there since it is apparently not possible to call the template * function std::string::resize(). * See the dumpStd_W_String() how to figure out if the internal buffer * or an allocated array is used. */ const SymbolGroupValue v(n, ctx); SymbolGroupValue base = v; SymbolGroupValue bx = base["_Bx"]; if (!bx) { base = base[unsigned(0)]; bx = base["_Bx"]; } if (!bx) { base = base[unsigned(0)][unsigned(1)]; bx = base["_Bx"]; } if (!bx) return 24; SymbolGroupValue size = base["_Mysize"]; int reserved = base["_Myres"].intValue(); if (reserved < 0 || !size || !bx) return 42; if (reserved <= (int)data.size()) return 1; // Insufficient memory. // Copy data: 'Buf' array for small strings, else pointer 'Ptr'. const int bufSize = type == KT_StdString ? 16 : 8; // see basic_string. const ULONG64 address = (bufSize <= reserved) ? bx["_Ptr"].pointerValue() : bx["_Buf"].address(); if (!address) return 3; if (!SymbolGroupValue::writeMemory(v.context().dataspaces, address, (const unsigned char *)(data.c_str()), ULONG(data.empty() ? 0 : sizeof(data.front()) * data.size()))) return 7; // Correct size if (!size.node()->assign(toString(data.size()))) return 13; return 0; } // assignment of std::string assign via ASCII, std::wstring via UTF16 static inline bool assignStdString(SymbolGroupNode *n, int type, const std::string &value, const SymbolGroupValueContext &ctx, std::string *errorMessage) { const int errorCode = type == KT_StdString ? assignStdStringI(n, type, value, ctx) : assignStdStringI(n, type, utf8ToUtf16(value), ctx); if (errorCode) { *errorMessage = msgAssignStringFailed(value, errorCode); return false; } return true; } bool assignType(SymbolGroupNode *n, int knownType, const std::string &value, const SymbolGroupValueContext &ctx, std::string *errorMessage) { switch (knownType) { case KT_QString: return assignQString(n, value, ctx, errorMessage); case KT_QByteArray: return assignQByteArray(n,value, ctx, errorMessage); case KT_StdString: case KT_StdWString: return assignStdString(n, knownType, value, ctx, errorMessage); default: break; } return false; } std::vector dumpComplexType(SymbolGroupNode *n, int type, void *specialInfo, const SymbolGroupValueContext &ctx) { std::vector rc; if (!(type & KT_HasComplexDumper)) return rc; switch (type) { case KT_QByteArray: rc = complexDumpQByteArray(n, ctx); break; case KT_QRegion: if (specialInfo) { typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector NodeVector; NodeVector children = reinterpret_cast(specialInfo)->children(); for (NodeVector::iterator it = children.begin(); it != children.end(); ++it) { if (SymbolGroupNode *node = (*it)->asSymbolGroupNode()) rc.push_back(new ReferenceSymbolGroupNode(node->name(), node->iName(), node)); } } break; case KT_QWidget: // Special info by simple dumper is the QWidgetPrivate node case KT_QWindow: // Special info by simple dumper is the QWindowPrivate node case KT_QObject: // Special info by simple dumper is the QObjectPrivate node if (specialInfo) { SymbolGroupNode *qObjectPrivateNode = reinterpret_cast(specialInfo); rc.push_back(new ReferenceSymbolGroupNode("d", "d", qObjectPrivateNode)); } break; case KT_QVariant: // Special info by simple dumper is the container (stringlist, map,etc) if (specialInfo) { SymbolGroupNode *containerNode = reinterpret_cast(specialInfo); rc.push_back(new ReferenceSymbolGroupNode("children", "children", containerNode)); } case KT_QWeakPointer: case KT_QSharedPointer: // Special info by simple dumper is the value if (specialInfo) { SymbolGroupNode *valueNode = reinterpret_cast(specialInfo); rc.push_back(new ReferenceSymbolGroupNode("value", "value", valueNode)); } break; default: break; } if (SymbolGroupValue::verbose) { DebugPrint dp; dp << "name(); } return rc; }