From bc31a3f140d765056989d84b2fb90bc4635d7652 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 16 Dec 2010 15:49:08 +0100 Subject: [PATCH] Debugger[New CDB]: Dump QHash/QMultiHash/QSet. Introduce new Symbol group node for fake map nodes. Iterate over QHash and extract keys, values for QSet/QHash. --- src/libs/qtcreatorcdbext/containers.cpp | 150 +++++++++++++++++++++++++- src/libs/qtcreatorcdbext/symbolgroup.h | 11 +- src/libs/qtcreatorcdbext/symbolgroupnode.cpp | 64 ++++++++++- src/libs/qtcreatorcdbext/symbolgroupnode.h | 31 +++++- src/libs/qtcreatorcdbext/symbolgroupvalue.cpp | 31 ++++-- 5 files changed, 262 insertions(+), 25 deletions(-) diff --git a/src/libs/qtcreatorcdbext/containers.cpp b/src/libs/qtcreatorcdbext/containers.cpp index 13d7b7b452..e8e25fe50b 100644 --- a/src/libs/qtcreatorcdbext/containers.cpp +++ b/src/libs/qtcreatorcdbext/containers.cpp @@ -35,6 +35,8 @@ #include typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector; +typedef std::vector SymbolGroupValueVector; +typedef std::vector::size_type VectorIndexType; // Read a pointer array from debuggee memory (ULONG64/32 according pointer size) static void *readPointerArray(ULONG64 address, unsigned count, const SymbolGroupValueContext &ctx) @@ -394,12 +396,152 @@ static inline AbstractSymbolGroupNodePtrVector innerType, count); } +// Return the list of buckets of a 'QHash<>' as 'QHashData::Node *' values from +// the list of addresses passed in +template +SymbolGroupValueVector hashBuckets(SymbolGroup *sg, const std::string hashNodeType, + const AddressType *pointerArray, + int numBuckets, + AddressType ePtr, + const SymbolGroupValueContext &ctx) +{ + SymbolGroupValueVector rc; + rc.reserve(numBuckets); + const AddressType *end = pointerArray + numBuckets; + std::string errorMessage; + // Skip 'e' special values as they are used as placeholder for reserve(d) + // empty array elements. + for (const AddressType *p = pointerArray; p < end; p++) { + if (*p != ePtr) { + const std::string name = pointedToSymbolName(*p, hashNodeType); + if (SymbolGroupNode *child = sg->addSymbol(name, std::string(), &errorMessage)) { + rc.push_back(SymbolGroupValue(child, ctx)); + } else { + return std::vector(); + break; + } + } + } + return rc; +} + +// Return real node type of a QHash: "class QHash" -> [struct] "QHashNode"; +static inline std::string qHashNodeType(std::string qHashType) +{ + qHashType.erase(0, 6); // Strip "class "; + const std::string::size_type pos = qHashType.find('<'); + if (pos != std::string::npos) + qHashType.insert(pos, "Node"); + return qHashType; +} + +// Return up to count nodes of type "QHashNode" of a "class QHash". +SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v, + VectorIndexType count) +{ + if (!count) + return SymbolGroupValueVector(); + const SymbolGroupValue hashData = v["d"]; + // 'e' is used as a special value to indicate empty hash buckets in the array. + const ULONG64 ePtr = v["e"].pointerValue(); + if (!hashData || !ePtr) + return SymbolGroupValueVector(); + // Retrieve the array of buckets of 'd' + const int numBuckets = hashData["numBuckets"].intValue(); + const ULONG64 bucketArray = hashData["buckets"].pointerValue(); + if (numBuckets <= 0 || !bucketArray) + return SymbolGroupValueVector(); + void *bucketPointers = readPointerArray(bucketArray, numBuckets, v.context()); + if (!bucketPointers) + return SymbolGroupValueVector(); + // Get list of buckets (starting elements of 'QHashData::Node') + const std::string dummyNodeType = "QHashData::Node"; + const SymbolGroupValueVector buckets = SymbolGroupValue::pointerSize() == 8 ? + hashBuckets(v.node()->symbolGroup(), dummyNodeType, + reinterpret_cast(bucketPointers), numBuckets, + ePtr, v.context()) : + hashBuckets(v.node()->symbolGroup(), dummyNodeType, + reinterpret_cast(bucketPointers), numBuckets, + ULONG32(ePtr), v.context()); + delete [] bucketPointers ; + // Generate the list 'QHashData::Node *' by iterating over the linked list of + // nodes starting at each bucket. Using the 'QHashData::Node *' instead of + // the 'QHashNode' is much faster. Each list has a trailing, unused + // dummy element. + SymbolGroupValueVector dummyNodeList; + dummyNodeList.reserve(count); + bool notEnough = true; + const SymbolGroupValueVector::const_iterator ncend = buckets.end(); + for (SymbolGroupValueVector::const_iterator it = buckets.begin(); notEnough && it != ncend; ++it) { + for (SymbolGroupValue l = *it; notEnough && l ; ) { + const SymbolGroupValue next = l["next"]; + if (next && next.pointerValue()) { // Stop at trailing dummy element + dummyNodeList.push_back(l); + if (dummyNodeList.size() >= count) // Stop at maximum count + notEnough = false; + } else { + break; + } + } + } + // Finally convert them into real nodes 'QHashNode (potentially expensive) + const std::string nodeType = qHashNodeType(v.type()); + SymbolGroupValueVector nodeList; + nodeList.reserve(count); + const SymbolGroupValueVector::const_iterator dcend = dummyNodeList.end(); + for (SymbolGroupValueVector::const_iterator it = dummyNodeList.begin(); it != dcend; ++it) { + if (const SymbolGroupValue n = (*it).typeCast(nodeType.c_str())) { + nodeList.push_back(n); + } else { + return SymbolGroupValueVector(); + } + } + return nodeList; +} + +// QSet<>: Contains a 'QHash' as member 'q_hash'. +// Just dump the keys as an array. static inline AbstractSymbolGroupNodePtrVector - qHashChildList(const SymbolGroupValue &, int count) + qSetChildList(const SymbolGroupValue &v, VectorIndexType count) +{ + const SymbolGroupValue qHash = v["q_hash"]; + AbstractSymbolGroupNodePtrVector rc; + if (!count || !qHash) + return rc; + const SymbolGroupValueVector nodes = qHashNodes(qHash, count); + if (nodes.size() != VectorIndexType(count)) + return rc; + rc.reserve(count); + for (int i = 0; i < count; i++) { + if (const SymbolGroupValue key = nodes.at(i)["key"]) { + rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, key.node())); + } else { + return AbstractSymbolGroupNodePtrVector(); + } + } + return rc; +} + +// QHash<>: Add with fake map nodes. +static inline AbstractSymbolGroupNodePtrVector + qHashChildList(const SymbolGroupValue &v, VectorIndexType count) { AbstractSymbolGroupNodePtrVector rc; if (!count) return rc; + const SymbolGroupValueVector nodes = qHashNodes(v, count); + if (nodes.size() != count) + return rc; + rc.reserve(count); + for (int i = 0; i < count; i++) { + const SymbolGroupValue &mapNode = nodes.at(i); + const SymbolGroupValue key = mapNode["key"]; + const SymbolGroupValue value = mapNode["value"]; + if (!key || !value) + return AbstractSymbolGroupNodePtrVector(); + rc.push_back(MapNodeSymbolGroupNode::create(i, mapNode.address(), + mapNode.type(), key.node(), value.node())); + } return rc; } @@ -429,14 +571,12 @@ AbstractSymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int ty break; case KT_QHash: return qHashChildList(SymbolGroupValue(node, ctx), size); - case KT_QMultiMap: + case KT_QMultiHash: if (const SymbolGroupValue hash = SymbolGroupValue(node, ctx)[unsigned(0)]) return qHashChildList(hash, size); break; case KT_QSet: - if (const SymbolGroupValue qHash = SymbolGroupValue(node, ctx)["q_hash"]) - return qHashChildList(qHash, size); - break; + return qSetChildList(SymbolGroupValue(node, ctx), size); case KT_QStringList: if (const SymbolGroupValue qList = SymbolGroupValue(node, ctx)[unsigned(0)]) return qListChildList(qList, size); diff --git a/src/libs/qtcreatorcdbext/symbolgroup.h b/src/libs/qtcreatorcdbext/symbolgroup.h index ce042ae7f3..67be5b52e0 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.h +++ b/src/libs/qtcreatorcdbext/symbolgroup.h @@ -33,10 +33,13 @@ #include "common.h" #include "symbolgroupnode.h" -// Thin wrapper around a symbol group storing a tree of expanded symbols rooted on -// a fake "locals" root element. -// Provides a find() method based on inames ("locals.this.i1.data") that retrieves -// that index based on the current expansion state. +/* A symbol group storing a tree of expanded symbols rooted on + * a fake "locals" root element. + * Provides a find() method based on inames ("locals.this.i1.data") and + * dump() methods used for GDBMI-format dumping and debug helpers. + * Qt Creator's WatchModel is fed from this class. It basically represents the + * symbol group tree with some additional node types (Reference and Map Node + * types. */ class SymbolGroup { public: diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp index d89fbabd6d..64755c50e3 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp @@ -1023,6 +1023,20 @@ SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name, } // --------- ReferenceSymbolGroupNode + +// Utility returning a pair ('[42]','42') as name/iname pair +// for a node representing an array index +typedef std::pair StringStringPair; + +static inline StringStringPair arrayIndexNameIname(int index) +{ + StringStringPair rc(std::string(), toString(index)); + rc.first = std::string(1, '['); + rc.first += rc.second; + rc.first.push_back(']'); + return rc; +} + ReferenceSymbolGroupNode::ReferenceSymbolGroupNode(const std::string &name, const std::string &iname, SymbolGroupNode *referencedNode) : @@ -1034,11 +1048,8 @@ ReferenceSymbolGroupNode::ReferenceSymbolGroupNode(const std::string &name, ReferenceSymbolGroupNode *ReferenceSymbolGroupNode::createArrayNode(int index, SymbolGroupNode *referencedNode) { - const std::string iname = toString(index); - std::string name = std::string(1, '['); - name += iname; - name.push_back(']'); - return new ReferenceSymbolGroupNode(name, iname, referencedNode); + const StringStringPair nameIname = arrayIndexNameIname(index); + return new ReferenceSymbolGroupNode(nameIname.first, nameIname.second, referencedNode); } void ReferenceSymbolGroupNode::dump(std::ostream &str, const std::string &visitingFullIname, @@ -1056,6 +1067,49 @@ void ReferenceSymbolGroupNode::debug(std::ostream &str, const std::string &visit m_referencedNode->debug(str, visitingFullIname, verbosity, depth); } +// ---------------- MapNodeSymbolGroupNode +MapNodeSymbolGroupNode::MapNodeSymbolGroupNode(const std::string &name, + const std::string &iname, + ULONG64 address, + const std::string &type, + AbstractSymbolGroupNode *key, + AbstractSymbolGroupNode *value) : + BaseSymbolGroupNode(name, iname), m_address(address), m_type(type) +{ + addChild(key); + addChild(value); +} + +MapNodeSymbolGroupNode + *MapNodeSymbolGroupNode::create(int index, ULONG64 address, + const std::string &type, + SymbolGroupNode *key, SymbolGroupNode *value) +{ + const StringStringPair nameIname = arrayIndexNameIname(index); + const std::string keyName = "key"; + ReferenceSymbolGroupNode *keyRN = new ReferenceSymbolGroupNode(keyName, keyName, key); + const std::string valueName = "value"; + ReferenceSymbolGroupNode *valueRN = new ReferenceSymbolGroupNode(valueName, valueName, value); + return new MapNodeSymbolGroupNode(nameIname.first, nameIname.second, address, type, keyRN, valueRN); +} + +void MapNodeSymbolGroupNode::dump(std::ostream &str, const std::string &visitingFullIname, + const DumpParameters &, const SymbolGroupValueContext &) +{ + SymbolGroupNode::dumpBasicData(str, name(), visitingFullIname); + if (m_address) + str << ",address=\"0x" << std::hex << m_address << '"'; + str << ",type=\"" << m_type << "\",valueencoded=\"0\",value=\"\",valueenabled=\"false\"" + ",valueeditable=\"false\",numchild=\"2\""; +} + +void MapNodeSymbolGroupNode::debug(std::ostream &os, const std::string &visitingFullIname, + unsigned /* verbosity */, unsigned depth) const +{ + indentStream(os, 2 * depth); + os << "MapNode " << name() << '/' << visitingFullIname << '\n'; +} + // --------- DebugSymbolGroupNodeVisitor // "local.vi" -> "local" diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.h b/src/libs/qtcreatorcdbext/symbolgroupnode.h index 88350741a2..dcb457c9ea 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupnode.h +++ b/src/libs/qtcreatorcdbext/symbolgroupnode.h @@ -159,7 +159,8 @@ private: void removeChildren(); }; -/* SymbolGroupNode: 'Real' node within a symbol group, identified by its index. +/* SymbolGroupNode: 'Real' node within a symbol group, identified by its index + * in IDebugSymbolGroup. * Provides accessors for fixed-up symbol group value and a dumping facility * consisting of: * - 'Simple' dumping done when running the DumpVisitor. This produces one @@ -265,7 +266,7 @@ private: // Artificial node referencing another (real) SymbolGroupNode (added symbol or // symbol from within a linked list structure. Forwards dumping to referenced node -// using its own name/iname. +// using its own name. class ReferenceSymbolGroupNode : public AbstractSymbolGroupNode { public: @@ -290,6 +291,32 @@ private: SymbolGroupNode * const m_referencedNode; }; +// Base class for a [fake] map node with a fake array index and key/value entries. +class MapNodeSymbolGroupNode : public BaseSymbolGroupNode +{ +private: + explicit MapNodeSymbolGroupNode(const std::string &name, + const std::string &iname, + ULONG64 address /* = 0 */, + const std::string &type, + AbstractSymbolGroupNode *key, + AbstractSymbolGroupNode *value); + +public: + static MapNodeSymbolGroupNode * + create(int i, ULONG64 address /* = 0 */, const std::string &type, + SymbolGroupNode *key, SymbolGroupNode *value); + + virtual void dump(std::ostream &str, const std::string &visitingFullIname, + const DumpParameters &p, const SymbolGroupValueContext &ctx); + virtual void debug(std::ostream &os, const std::string &visitingFullIname, + unsigned verbosity, unsigned depth) const; + +private: + const ULONG64 m_address; + const std::string m_type; +}; + /* Visitor that takes care of iterating over the nodes and * building the full iname path ('local.foo.bar') that is required for * GDBMI dumping. The full name depends on the path on which a node was reached diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp index 58e733637b..c2513670a5 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp @@ -189,15 +189,28 @@ SymbolGroupValue SymbolGroupValue::pointerTypeCast(const char *type) const SymbolGroupValue SymbolGroupValue::typeCastedValue(ULONG64 address, const char *type) const { - if (address) { - SymbolGroup *sg = m_node->symbolGroup(); - std::ostringstream str; - str << '(' << type << ")(" << std::showbase << std::hex << address << ')'; - if (SymbolGroupNode *node = sg->addSymbol(str.str(), - additionalSymbolIname(sg), - &m_errorMessage)) - return SymbolGroupValue(node, m_context); - } + 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(str.str(), + additionalSymbolIname(sg), + &m_errorMessage)) + return SymbolGroupValue(node, m_context); return SymbolGroupValue(); } -- 2.11.0