Debugger[New CDB]: First attempts at QMap<>.

Qualify map and hash nodes as Module!QMapNode<>.
Limit recursion depth of GDBMI dump.
Introduce error reporting.
Iterate QMap nodes and create artificial key/value nodes.
This commit is contained in:
Friedemann Kleint
2010-12-21 17:23:27 +01:00
parent da3a5360df
commit 8d3e48af33
9 changed files with 451 additions and 37 deletions

View File

@@ -732,16 +732,25 @@ SymbolGroupValueVector hashBuckets(SymbolGroup *sg, const std::string hashNodeTy
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)
// Return the node type of a QHash/QMap:
// "class QHash<K,V>" -> [struct] "QtCored4!QHashNode<K,V>";
static inline std::string qHashNodeType(const SymbolGroupValue &v,
const char *nodeType)
{
qHashType.erase(0, 6); // Strip "class ";
std::string qHashType = v.type();
const std::string::size_type pos = qHashType.find('<');
if (pos != std::string::npos)
qHashType.insert(pos, "Node");
return qHashType;
qHashType.insert(pos, nodeType);
// A map node must be qualified with the current module and
// the Qt namespace (particularly QMapNode, QHashNodes work also for
// the unqualified case).
const QtInfo &qtInfo = QtInfo::get(v.context());
const std::string currentModule = v.node()->symbolGroup()->module();
return QtInfo::prependModuleAndNameSpace(qHashType, currentModule, qtInfo.nameSpace);
}
enum { debugMap = 0 };
// Return up to count nodes of type "QHashNode<K,V>" of a "class QHash<K,V>".
SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
VectorIndexType count)
@@ -751,6 +760,8 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
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 (debugMap)
DebugPrint() << v << " Count=" << count << ",ePtr=0x" << std::hex << ePtr;
if (!hashData || !ePtr)
return SymbolGroupValueVector();
// Retrieve the array of buckets of 'd'
@@ -762,7 +773,7 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
if (!bucketPointers)
return SymbolGroupValueVector();
// Get list of buckets (starting elements of 'QHashData::Node')
const std::string dummyNodeType = "QHashData::Node";
const std::string dummyNodeType = ntext()).prependQtCoreModule("QHashData::Node");
const SymbolGroupValueVector buckets = SymbolGroupValue::pointerSize() == 8 ?
hashBuckets(v.node()->symbolGroup(), dummyNodeType,
reinterpret_cast<const ULONG64 *>(bucketPointers), numBuckets,
@@ -786,13 +797,18 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
dummyNodeList.push_back(l);
if (dummyNodeList.size() >= count) // Stop at maximum count
notEnough = false;
if (debugMap)
DebugPrint() << '#' << (dummyNodeList.size() - 1) << "l=" << l << ",next=" << next;
l = next;
} else {
break;
}
}
}
// Finally convert them into real nodes 'QHashNode<K,V> (potentially expensive)
const std::string nodeType = qHashNodeType(v.type());
const std::string nodeType = qHashNodeType(v, "Node");
if (debugMap)
DebugPrint() << "Converting into " << nodeType;
SymbolGroupValueVector nodeList;
nodeList.reserve(count);
const SymbolGroupValueVector::const_iterator dcend = dummyNodeList.end();
@@ -809,7 +825,7 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
// QSet<>: Contains a 'QHash<key, QHashDummyValue>' as member 'q_hash'.
// Just dump the keys as an array.
static inline AbstractSymbolGroupNodePtrVector
qSetChildList(const SymbolGroupValue &v, VectorIndexType count)
qSetChildList(const SymbolGroupValue &v, int count)
{
const SymbolGroupValue qHash = v["q_hash"];
AbstractSymbolGroupNodePtrVector rc;
@@ -831,7 +847,7 @@ static inline AbstractSymbolGroupNodePtrVector
// QHash<>: Add with fake map nodes.
static inline AbstractSymbolGroupNodePtrVector
qHashChildList(const SymbolGroupValue &v, VectorIndexType count)
qHashChildList(const SymbolGroupValue &v, int count)
{
AbstractSymbolGroupNodePtrVector rc;
if (!count)
@@ -852,6 +868,106 @@ static inline AbstractSymbolGroupNodePtrVector
return rc;
}
// QMap<>: Return the list of QMapData::Node
static inline SymbolGroupValueVector qMapNodes(const SymbolGroupValue &v, VectorIndexType count)
{
const SymbolGroupValue e = v["e"];
const ULONG64 ePtr = e.pointerValue();
if (debugMap)
DebugPrint() << v.type() << " E=0x" << std::hex << ePtr;
if (!ePtr)
return SymbolGroupValueVector();
if (debugMap)
DebugPrint() << v.type() << " E=0x" << std::hex << ePtr;
SymbolGroupValueVector rc;
rc.reserve(count);
SymbolGroupValue n = e["forward"][unsigned(0)];
for (VectorIndexType i = 0; i < count && n && n.pointerValue() != ePtr; i++) {
rc.push_back(n);
n = n["forward"][unsigned(0)];
}
return rc;
}
// QMap<>: Add with fake map nodes.
static inline AbstractSymbolGroupNodePtrVector
qMapChildList(const SymbolGroupValue &v, VectorIndexType count)
{
if (debugMap)
DebugPrint() << v.type() << "," << count;
if (!count)
return AbstractSymbolGroupNodePtrVector();
// Get node type: 'class namespace::QMap<K,T>'
// ->'QtCored4!namespace::QMapNode<K,T>'
// Note: Any types QMapNode<> will not be found without modules!
const std::string mapNodeType = qHashNodeType(v, "Node");
const std::string mapPayloadNodeType = qHashNodeType(v, "PayloadNode");
// Calculate the offset needed (see QMap::concrete() used by the iterator).
const unsigned mapNodeSize = SymbolGroupValue::sizeOf(mapPayloadNodeType.c_str());
const unsigned payloadNodeSize = SymbolGroupValue::sizeOf(mapPayloadNodeType.c_str());
const unsigned pointerSize = SymbolGroupValue::pointerSize();
if (debugMap) {
DebugPrint() << v.type() << "," << mapNodeType << ':'
<< mapNodeSize << ',' << mapPayloadNodeType << ':' << payloadNodeSize
<< ", pointerSize=" << pointerSize;
}
if (!payloadNodeSize || !mapNodeSize)
return AbstractSymbolGroupNodePtrVector();
const ULONG64 payLoad = payloadNodeSize - pointerSize;
// Get the value offset. Manually determine the alignment to be able
// to retrieve key/value without having to deal with QMapNode<> (see below).
// Subtract the 2 trailing pointers of the node.
const std::vector<std::string> innerTypes = v.innerTypes();
if (innerTypes.size() != 2u)
return AbstractSymbolGroupNodePtrVector();
const unsigned valueSize = SymbolGroupValue::sizeOf(innerTypes.at(1).c_str());
const unsigned valueOffset = mapNodeSize - valueSize - pointerSize;
if (debugMap)
DebugPrint() << "Payload=" << payLoad << ",valueOffset=" << valueOffset << ','
<< innerTypes.front() << ',' << innerTypes.back() << ':' << valueSize;
if (!valueOffset || !valueSize)
return AbstractSymbolGroupNodePtrVector();
// Get the children.
const SymbolGroupValueVector childNodes = qMapNodes(v, count);
if (debugMap)
DebugPrint() << "children: " << childNodes.size() << " of " << count;
// Deep expansion of the forward[0] sometimes fails. In that case,
// take what we can get.
if (childNodes.size() != count)
count = childNodes.size();
// The correct way of doing this would be to construct additional symbols
// '*(QMapNode<K,V> *)(node_address)'. However, when doing this as of
// 'CDB 6.12.0002.633' (21.12.2010) IDebugSymbolGroup::AddSymbol()
// just fails, returning DEBUG_ANY_ID without actually doing something. So,
// we circumvent the map nodes and directly create key and values at their addresses.
AbstractSymbolGroupNodePtrVector rc;
rc.reserve(count);
std::string errorMessage;
SymbolGroup *sg = v.node()->symbolGroup();
for (VectorIndexType i = 0; i < count ; i++) {
const ULONG64 nodePtr = childNodes.at(i).pointerValue();
if (!nodePtr)
return AbstractSymbolGroupNodePtrVector();
const ULONG64 keyAddress = nodePtr - payLoad;
const std::string keyExp = pointedToSymbolName(keyAddress, innerTypes.front());
const std::string valueExp = pointedToSymbolName(keyAddress + valueOffset, innerTypes.at(1));
if (debugMap) {
DebugPrint() << '#' << i << '/' << count << ' ' << std::hex << ",node=0x" << nodePtr <<
',' <<keyExp << ',' << valueExp;
}
// Create the nodes
SymbolGroupNode *keyNode = sg->addSymbol(keyExp, std::string(), &errorMessage);
SymbolGroupNode *valueNode = sg->addSymbol(valueExp, std::string(), &errorMessage);
if (!keyNode || !valueNode)
return AbstractSymbolGroupNodePtrVector();
rc.push_back(MapNodeSymbolGroupNode::create(int(i), keyAddress,
mapNodeType, keyNode, valueNode));
}
return rc;
}
AbstractSymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int type,
int size, const SymbolGroupValueContext &ctx)
{
@@ -884,6 +1000,12 @@ AbstractSymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int ty
break;
case KT_QSet:
return qSetChildList(SymbolGroupValue(node, ctx), size);
case KT_QMap:
return qMapChildList(SymbolGroupValue(node, ctx), size);
case KT_QMultiMap:
if (const SymbolGroupValue qmap = SymbolGroupValue(node, ctx)[unsigned(0)])
return qMapChildList(qmap, size);
break;
case KT_QStringList:
if (const SymbolGroupValue qList = SymbolGroupValue(node, ctx)[unsigned(0)])
return qListChildList(qList, size);

View File

@@ -109,9 +109,10 @@ static const CommandDescription commandDescriptions[] = {
"-c complex dumpers"},
{"locals",
"Prints local variables of symbol group in GDBMI or debug format",
"[-t token] [T formats] [-I formats] [-c] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n<frame-number> [iname]\n"
"[-t token] [T formats] [-I formats] [-f debugfilter] [-c] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n<frame-number> [iname]\n"
"-h human-readable ouput\n"
"-d debug output\n"
"-f debug_filter\n"
"-c complex dumpers\n"
"-e expand-list Comma-separated list of inames to be expanded beforehand\n"
"-u uninitialized-list Comma-separated list of uninitialized inames\n"
@@ -281,7 +282,7 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
}
const unsigned succeeded = runComplexDumpers ?
symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces()), &errorMessage) :
symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &errorMessage) :
symGroup->expandList(inames, &errorMessage);
ExtensionContext::instance().report('R', token, "expandlocals", "%u/%u %s",
@@ -298,6 +299,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
// Parse the command
unsigned debugOutput = 0;
std::string iname;
std::string debugFilter;
StringList tokens = commandTokens<StringList>(args, token);
StringVector expandedInames;
StringVector uninitializedInames;
@@ -324,6 +326,14 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
split(tokens.front(), ',', std::back_inserter(uninitializedInames));
tokens.pop_front();
break;
case 'f':
if (tokens.empty()) {
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
return std::string();
}
debugFilter = tokens.front();
tokens.pop_front();
break;
case 'e':
if (tokens.empty()) {
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
@@ -362,7 +372,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
if (!tokens.empty())
iname = tokens.front();
const SymbolGroupValueContext dumpContext(exc.dataSpaces());
const SymbolGroupValueContext dumpContext(exc.dataSpaces(), exc.symbols());
SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage);
if (!symGroup)
return std::string();
@@ -379,7 +389,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
}
if (debugOutput)
return symGroup->debug(iname, debugOutput - 1);
return symGroup->debug(iname, debugFilter, debugOutput - 1);
return iname.empty() ?
symGroup->dump(dumpContext, parameters) :
@@ -430,7 +440,7 @@ static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int
return std::string();
}
std::wstring value;
if (!dumpSimpleType(n->asSymbolGroupNode(), SymbolGroupValueContext(exc.dataSpaces()), &value)) {
if (!dumpSimpleType(n->asSymbolGroupNode(), SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &value)) {
*errorMessage = "Cannot dump " + iname;
return std::string();
}

View File

@@ -37,6 +37,7 @@
#include <string>
#include <sstream>
#include <map>
#include <functional>
void trimFront(std::string &s);
void trimBack(std::string &s);
@@ -58,6 +59,18 @@ void split(const std::string &s, char sep, Iterator it)
}
}
// A boolean predicate that can be used for grepping sequences
// of strings for a 'needle' substring.
class SubStringPredicate : public std::unary_function<const std::string &, bool>
{
public:
explicit SubStringPredicate(const std::string &needle) : m_needle(needle) {}
bool operator()(const std::string &s) { return s.find(m_needle) != std::string::npos; }
private:
const std::string &m_needle;
};
// Format numbers, etc, as a string.
template <class Streamable>
std::string toString(const Streamable s)

View File

@@ -33,10 +33,12 @@
#include "symbolgroup.h"
#include "stringutils.h"
#include "gdbmihelpers.h"
#include <set>
#include <algorithm>
#include <iterator>
#include <memory>
typedef std::vector<int>::size_type VectorIndexType;
typedef std::vector<std::string> StringVector;
@@ -48,13 +50,21 @@ const char rootNameC[] = "local";
SymbolGroup::SymbolGroup(IDebugSymbolGroup2 *sg,
const SymbolParameterVector &vec,
ULONG threadId,
unsigned frame) :
unsigned frame,
const std::string &function) :
m_symbolGroup(sg),
m_threadId(threadId),
m_frame(frame),
m_root(0)
m_root(0),
m_function(function)
{
m_root = SymbolGroupNode::create(this, rootNameC, vec);
// Split function 'Mod!foo'
const std::string::size_type exclPos = m_function.find('!');
if (exclPos != std::string::npos) {
m_module = m_function.substr(0, exclPos);
m_function.erase(0, exclPos + 1);
}
}
SymbolGroup::~SymbolGroup()
@@ -135,6 +145,7 @@ SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugS
IDebugSymbolGroup2 *idebugSymbols = 0;
bool success = false;
SymbolParameterVector parameters;
std::string func;
// Obtain symbol group at stack frame.
do {
@@ -149,6 +160,11 @@ SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugS
*errorMessage = str.str();
break;
}
StackFrame frameData;
if (!getFrame(frame, &frameData, errorMessage))
break;
func = wStringToString(frameData.function);
hr = debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &idebugSymbols);
if (FAILED(hr)) {
*errorMessage = msgDebugEngineComFailed("GetScopeSymbolGroup2", hr);
@@ -176,7 +192,7 @@ SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugS
idebugSymbols->Release();
return 0;
}
return new SymbolGroup(idebugSymbols, parameters, threadId, frame);
return new SymbolGroup(idebugSymbols, parameters, threadId, frame, func);
}
static inline std::string msgNotFound(const std::string &nodeName)
@@ -246,16 +262,21 @@ std::string SymbolGroup::dump(const std::string &iname,
return str.str();
}
std::string SymbolGroup::debug(const std::string &iname, unsigned verbosity) const
std::string SymbolGroup::debug(const std::string &iname,
const std::string &filter,
unsigned verbosity) const
{
std::ostringstream str;
str << '\n';
DebugSymbolGroupNodeVisitor visitor(str, verbosity);
std::auto_ptr<DebugSymbolGroupNodeVisitor>
visitor(filter.empty() ?
new DebugSymbolGroupNodeVisitor(str, verbosity) :
new DebugFilterSymbolGroupNodeVisitor(str, filter, verbosity));
if (iname.empty()) {
accept(visitor);
accept(*visitor);
} else {
if (AbstractSymbolGroupNode *const node = find(iname)) {
node->accept(visitor, SymbolGroupNodeVisitor::parentIname(iname), 0, 0);
node->accept(*visitor, SymbolGroupNodeVisitor::parentIname(iname), 0, 0);
} else {
str << msgNotFound(iname);
}

View File

@@ -55,7 +55,8 @@ private:
explicit SymbolGroup(CIDebugSymbolGroup *,
const SymbolParameterVector &vec,
ULONG threadId, unsigned frame);
ULONG threadId, unsigned frame,
const std::string &function);
public:
typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector;
@@ -73,9 +74,13 @@ public:
// Expand node and dump
std::string dump(const std::string &iname, const SymbolGroupValueContext &ctx,
const DumpParameters &p, std::string *errorMessage);
std::string debug(const std::string &iname = std::string(), unsigned verbosity = 0) const;
std::string debug(const std::string &iname = std::string(),
const std::string &filter = std::string(),
unsigned verbosity = 0) const;
unsigned frame() const { return m_frame; }
std::string function() const { return m_function; }
std::string module() const { return m_module; }
ULONG threadId() const { return m_threadId; }
SymbolGroupNode *root() { return m_root; }
const SymbolGroupNode *root() const { return m_root; }
@@ -127,6 +132,8 @@ private:
const unsigned m_frame;
const ULONG m_threadId;
SymbolGroupNode *m_root;
std::string m_function;
std::string m_module;
};
#endif // SYMBOLGROUP_H

View File

@@ -37,6 +37,7 @@
#include "stringutils.h"
#include "base64.h"
#include "containers.h"
#include "extensioncontext.h"
#include <algorithm>
@@ -913,6 +914,15 @@ static inline std::string msgCannotCast(const std::string &nodeName,
return str.str();
}
static std::string msgExpandFailed(const std::string &name, const std::string &iname,
ULONG index, const std::string &why)
{
std::ostringstream str;
str << "Expansion of '" << name << "'/'" << iname << " (index: " << index
<< ") failed: " << why;
return str.str();
}
// Expand!
bool SymbolGroupNode::expand(std::string *errorMessage)
{
@@ -926,18 +936,21 @@ bool SymbolGroupNode::expand(std::string *errorMessage)
return true;
}
if (!canExpand()) {
*errorMessage = "No subelements to expand in node: " + absoluteFullIName();
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index,
"No subelements to expand in node.");
return false;
}
if (flags() & Uninitialized) {
*errorMessage = "Refusing to expand uninitialized node: " + absoluteFullIName();
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index,
"Refusing to expand uninitialized node.");
return false;
}
const HRESULT hr = m_symbolGroup->debugSymbolGroup()->ExpandSymbol(m_index, TRUE);
if (FAILED(hr)) {
*errorMessage = msgDebugEngineComFailed("ExpandSymbol", hr);
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, msgDebugEngineComFailed("ExpandSymbol", hr));
ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str());
return false;
}
SymbolGroup::SymbolParameterVector parameters;
@@ -945,8 +958,10 @@ bool SymbolGroupNode::expand(std::string *errorMessage)
// and corrected SubElement count (might be estimate))
if (!SymbolGroup::getSymbolParameters(m_symbolGroup->debugSymbolGroup(),
m_index, m_parameters.SubElements + 1,
&parameters, errorMessage))
&parameters, errorMessage)) {
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, *errorMessage);
return false;
}
// Before inserting children, correct indexes on whole group
m_symbolGroup->root()->notifyExpanded(m_index + 1, parameters.at(0).SubElements);
// Parse parameters, correct our own) and create child nodes.
@@ -1005,12 +1020,21 @@ SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name,
HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index);
if (FAILED(hr)) {
*errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("AddSymbol", hr));
ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str());
return 0;
}
if (index == DEBUG_ANY_ID) { // Occasionally happens for unknown or 'complicated' types
*errorMessage = msgCannotAddSymbol(name, "DEBUG_ANY_ID was returned as symbol index by AddSymbol.");
ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str());
return 0;
}
SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS());
hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(index, 1, &(*parameters.begin()));
if (FAILED(hr)) { // Should never fail
*errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("GetSymbolParameters", hr));
std::ostringstream str;
str << "Cannot retrieve 1 symbol parameter entry at " << index << ": "
<< msgDebugEngineComFailed("GetSymbolParameters", hr);
*errorMessage = msgCannotAddSymbol(name, str.str());
return 0;
}
// Paranoia: Check for cuckoo's eggs (which should not happen)
@@ -1137,6 +1161,24 @@ SymbolGroupNodeVisitor::VisitResult
return VisitContinue;
}
DebugFilterSymbolGroupNodeVisitor::DebugFilterSymbolGroupNodeVisitor(std::ostream &os,
const std::string &filter,
const unsigned verbosity) :
DebugSymbolGroupNodeVisitor(os, verbosity), m_filter(filter)
{
}
SymbolGroupNodeVisitor::VisitResult
DebugFilterSymbolGroupNodeVisitor::visit(AbstractSymbolGroupNode *node,
const std::string &fullIname,
unsigned child, unsigned depth)
{
if (fullIname.find(m_filter) == std::string::npos
&& node->name().find(m_filter) == std::string::npos)
return SymbolGroupNodeVisitor::VisitContinue;
return DebugSymbolGroupNodeVisitor::visit(node, fullIname, child, depth);
}
// --------------------- DumpSymbolGroupNodeVisitor
DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os,
const SymbolGroupValueContext &context,
@@ -1158,9 +1200,10 @@ SymbolGroupNodeVisitor::VisitResult
// Recurse to children only if expanded by explicit watchmodel request
// and initialized.
m_visitChildren = true;
// Visit children of a SymbolGroupNode only if not expanded by its dumpers
// Visit children of a SymbolGroupNode only if not expanded by its dumpers.
// Report only one level for Qt Creator.
if (SymbolGroupNode *snode = node->asSymbolGroupNode())
m_visitChildren = snode->isExpanded()
m_visitChildren = depth < 1 && snode->isExpanded()
&& (flags & (SymbolGroupNode::Uninitialized|SymbolGroupNode::ExpandedByDumper)) == 0;
// Comma between same level children given obscured children
if (depth == m_lastDepth) {

View File

@@ -352,7 +352,7 @@ protected:
VisitStop
};
private:
protected:
virtual VisitResult visit(AbstractSymbolGroupNode *node,
const std::string &fullIname,
unsigned child, unsigned depth) = 0;
@@ -365,15 +365,32 @@ class DebugSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
public:
explicit DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity = 0);
private:
protected:
virtual VisitResult visit(AbstractSymbolGroupNode *node,
const std::string &fullIname,
unsigned child, unsigned depth);
private:
std::ostream &m_os;
const unsigned m_verbosity;
};
// Debug filtering output visitor.
class DebugFilterSymbolGroupNodeVisitor : public DebugSymbolGroupNodeVisitor {
public:
explicit DebugFilterSymbolGroupNodeVisitor(std::ostream &os,
const std::string &filter,
const unsigned verbosity = 0);
protected:
virtual VisitResult visit(AbstractSymbolGroupNode *node,
const std::string &fullIname,
unsigned child, unsigned depth);
private:
const std::string m_filter;
};
// GDBMI dump output visitor.
class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
public:
@@ -381,12 +398,13 @@ public:
const SymbolGroupValueContext &context,
const DumpParameters &parameters = DumpParameters());
private:
protected:
virtual VisitResult visit(AbstractSymbolGroupNode *node,
const std::string &fullIname,
unsigned child, unsigned depth);
virtual void childrenVisited(const AbstractSymbolGroupNode * node, unsigned depth);
private:
std::ostream &m_os;
const SymbolGroupValueContext &m_context;
const DumpParameters &m_parameters;

View File

@@ -37,6 +37,14 @@
#include "containers.h"
#include <iomanip>
#include <algorithm>
SymbolGroupValue::SymbolGroupValue(const std::string &parentError) :
m_node(0), m_errorMessage(parentError)
{
if (m_errorMessage.empty())
m_errorMessage = "Invalid";
}
SymbolGroupValue::SymbolGroupValue(SymbolGroupNode *node,
const SymbolGroupValueContext &ctx) :
@@ -62,7 +70,7 @@ SymbolGroupValue SymbolGroupValue::operator[](unsigned index) const
if (index < m_node->children().size())
if (SymbolGroupNode *n = m_node->childAt(index)->asSymbolGroupNode())
return SymbolGroupValue(n, m_context);
return SymbolGroupValue();
return SymbolGroupValue(m_errorMessage);
}
bool SymbolGroupValue::ensureExpanded() const
@@ -88,7 +96,7 @@ SymbolGroupValue SymbolGroupValue::operator[](const char *name) const
if (AbstractSymbolGroupNode *child = m_node->childByIName(name))
if (SymbolGroupNode *n = child->asSymbolGroupNode())
return SymbolGroupValue(n, m_context);
return SymbolGroupValue();
return SymbolGroupValue(m_errorMessage);
}
std::string SymbolGroupValue::type() const
@@ -253,6 +261,12 @@ unsigned SymbolGroupValue::pointerSize()
return ps;
}
unsigned SymbolGroupValue::intSize()
{
static const unsigned is = SymbolGroupValue::sizeOf("int");
return is;
}
std::string SymbolGroupValue::stripPointerType(const std::string &t)
{
return isPointerType(t) ? t.substr(0, t.size() - 2) : t;
@@ -279,6 +293,145 @@ std::string SymbolGroupValue::stripArrayType(const std::string &t)
return t;
}
/* QtInfo helper: Determine the full name of a Qt Symbol like 'qstrdup' in 'QtCored4'.
* as 'QtCored4![namespace::]qstrdup'. In the event someone really uses a different
* library prefix or namespaced Qt, this should be found.
* The crux is here that the underlying IDebugSymbols::StartSymbolMatch()
* does not accept module wildcards (as opposed to the 'x' command where 'x QtCo*!*qstrdup'
* would be acceptable and fast). OTOH, doing a wildcard search like '*qstrdup' is
* very slow and should be done only if there is really a different namespace or lib prefix.
* Parameter 'modulePatternC' is used to do a search on the modules returned (due to
* the amiguities and artifacts that appear like 'QGuid4!qstrdup'). */
static inline std::string resolveQtSymbol(const char *symbolC,
const char *defaultModuleNameC,
const char *modulePatternC,
const SymbolGroupValueContext &ctx)
{
typedef std::list<std::string> StringList;
typedef StringList::const_iterator StringListConstIt;
// First try a match with the default module name 'QtCored4!qstrdup' for speed reasons
std::string defaultPattern = defaultModuleNameC;
defaultPattern.push_back('!');
defaultPattern += symbolC;
const StringList defaultMatches = SymbolGroupValue::resolveSymbol(defaultPattern.c_str(), ctx);
const SubStringPredicate modulePattern(modulePatternC);
const StringListConstIt defaultIt = std::find_if(defaultMatches.begin(), defaultMatches.end(), modulePattern);
if (defaultIt != defaultMatches.end())
return *defaultIt;
// Fail, now try a search with '*qstrdup' in all modules. This might return several matches
// like 'QtCored4!qstrdup', 'QGuid4!qstrdup'
const std::string wildCardPattern = std::string(1, '*') + symbolC;
const StringList allMatches = SymbolGroupValue::resolveSymbol(wildCardPattern.c_str(), ctx);
const StringListConstIt allIt = std::find_if(allMatches.begin(), allMatches.end(), modulePattern);
return allIt != allMatches.end() ? *allIt : std::string();
}
const QtInfo &QtInfo::get(const SymbolGroupValueContext &ctx)
{
static const char qtCoreDefaultModule[] = "QtCored4";
static QtInfo rc;
if (!rc.coreModule.empty())
return rc;
do {
// Lookup qstrdup() to hopefully get module and namespace
// Typically, this resolves to 'QtGuid4!qstrdup' and 'QtCored4!qstrdup'...
const std::string qualifiedSymbol = resolveQtSymbol("qstrdup", qtCoreDefaultModule, "Core", ctx);
if (qualifiedSymbol.empty()) {
rc.coreModule = qtCoreDefaultModule;
break;
}
// Should be 'QtCored4!qstrdup'
const std::string::size_type exclPos = qualifiedSymbol.find('!');
if (exclPos == std::string::npos) {
rc.coreModule = qtCoreDefaultModule;
break;
}
rc.coreModule = qualifiedSymbol.substr(0, exclPos);
// 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);
} while (false);
return rc;
}
std::string QtInfo::prependModuleAndNameSpace(const std::string &type,
const std::string &module,
const std::string &aNameSpace)
{
// Strip the prefixes "class ", "struct ".
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);
}
// 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;
}
std::list<std::string>
SymbolGroupValue::resolveSymbol(const char *pattern,
const SymbolGroupValueContext &c,
std::string *errorMessage /* = 0 */)
{
enum { bufSize = 2048 };
std::list<std::string> 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];
while (true) {
hr = c.symbols->GetNextSymbolMatch(handle, buf, bufSize - 1, 0, 0);
if (hr == E_NOINTERFACE)
break;
if (hr == S_OK)
rc.push_back(std::string(buf));
}
c.symbols->EndSymbolMatch(handle);
return rc;
}
// get the inner types: "QMap<int, double>" -> "int", "double"
std::vector<std::string> SymbolGroupValue::innerTypesOf(const std::string &t)
{

View File

@@ -39,16 +39,18 @@
#include <string>
#include <vector>
#include <list>
class SymbolGroupNode;
// Structure to pass all IDebug interfaces used for SymbolGroupValue
struct SymbolGroupValueContext
{
SymbolGroupValueContext(CIDebugDataSpaces *ds) : dataspaces(ds) {}
SymbolGroupValueContext::SymbolGroupValueContext() : dataspaces(0) {}
SymbolGroupValueContext(CIDebugDataSpaces *ds, CIDebugSymbols *s) : dataspaces(ds), symbols(s) {}
SymbolGroupValueContext::SymbolGroupValueContext() : dataspaces(0), symbols(0) {}
CIDebugDataSpaces *dataspaces;
CIDebugSymbols *symbols;
};
/* SymbolGroupValue: Flyweight tied to a SymbolGroupNode
@@ -57,6 +59,8 @@ struct SymbolGroupValueContext
class SymbolGroupValue
{
explicit SymbolGroupValue(const std::string &parentError);
public:
explicit SymbolGroupValue(SymbolGroupNode *node, const SymbolGroupValueContext &c);
SymbolGroupValue();
@@ -98,7 +102,11 @@ public:
static std::string addPointerType(const std::string &);
static std::string stripArrayType(const std::string &);
static bool isPointerType(const std::string &);
static std::list<std::string> resolveSymbol(const char *pattern,
const SymbolGroupValueContext &c,
std::string *errorMessage = 0);
static unsigned pointerSize();
static unsigned intSize();
// get the inner types: "QMap<int, double>" -> "int", "double"
static std::vector<std::string> innerTypesOf(const std::string &t);
@@ -115,6 +123,25 @@ private:
// For debugging purposes
std::ostream &operator<<(std::ostream &, const SymbolGroupValue &v);
// Qt Information: Namespace and module.
struct QtInfo
{
static const QtInfo &get(const SymbolGroupValueContext &ctx);
// Prepend core module and Qt namespace. To be able to work with some
// 'complicated' types like QMapNode, specifying the module helps
std::string prependQtCoreModule(const std::string &type) const
{ return QtInfo::prependModuleAndNameSpace(type, coreModule, nameSpace); }
// Prepend module and namespace if missing with some smartness
// ('Foo' or -> 'nsp::Foo') => 'QtCored4!nsp::Foo'
static std::string prependModuleAndNameSpace(const std::string &type,
const std::string &module,
const std::string &nameSpace);
std::string nameSpace;
std::string coreModule;
};
/* Helpers for detecting types reported from IDebugSymbolGroup
* 1) Class prefix==true is applicable to outer types obtained from
* from IDebugSymbolGroup: 'class foo' or 'struct foo'.