forked from qt-creator/qt-creator
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.
This commit is contained in:
@@ -35,6 +35,8 @@
|
||||
#include <functional>
|
||||
|
||||
typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector;
|
||||
typedef std::vector<SymbolGroupValue> SymbolGroupValueVector;
|
||||
typedef std::vector<int>::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<class AddressType>
|
||||
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<SymbolGroupValue>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Return real node type of a QHash: "class QHash<K,V>" -> [struct] "QHashNode<K,V>";
|
||||
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<K,V>" of a "class QHash<K,V>".
|
||||
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<const ULONG64 *>(bucketPointers), numBuckets,
|
||||
ePtr, v.context()) :
|
||||
hashBuckets(v.node()->symbolGroup(), dummyNodeType,
|
||||
reinterpret_cast<const ULONG32 *>(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<K,T>' 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<K,V> (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<key, QHashDummyValue>' 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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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<std::string, std::string> 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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -189,15 +189,28 @@ SymbolGroupValue SymbolGroupValue::pointerTypeCast(const char *type) const
|
||||
|
||||
SymbolGroupValue SymbolGroupValue::typeCastedValue(ULONG64 address, const char *type) const
|
||||
{
|
||||
if (address) {
|
||||
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;
|
||||
str << '(' << type << ")(" << std::showbase << std::hex << address << ')';
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user