Commit bc31a3f1 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

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.
parent 3d400854
......@@ -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) {
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();
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment