forked from qt-creator/qt-creator
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:
@@ -732,16 +732,25 @@ SymbolGroupValueVector hashBuckets(SymbolGroup *sg, const std::string hashNodeTy
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return real node type of a QHash: "class QHash<K,V>" -> [struct] "QHashNode<K,V>";
|
// Return the node type of a QHash/QMap:
|
||||||
static inline std::string qHashNodeType(std::string qHashType)
|
// "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('<');
|
const std::string::size_type pos = qHashType.find('<');
|
||||||
if (pos != std::string::npos)
|
if (pos != std::string::npos)
|
||||||
qHashType.insert(pos, "Node");
|
qHashType.insert(pos, nodeType);
|
||||||
return qHashType;
|
// 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>".
|
// Return up to count nodes of type "QHashNode<K,V>" of a "class QHash<K,V>".
|
||||||
SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
|
SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
|
||||||
VectorIndexType count)
|
VectorIndexType count)
|
||||||
@@ -751,6 +760,8 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
|
|||||||
const SymbolGroupValue hashData = v["d"];
|
const SymbolGroupValue hashData = v["d"];
|
||||||
// 'e' is used as a special value to indicate empty hash buckets in the array.
|
// 'e' is used as a special value to indicate empty hash buckets in the array.
|
||||||
const ULONG64 ePtr = v["e"].pointerValue();
|
const ULONG64 ePtr = v["e"].pointerValue();
|
||||||
|
if (debugMap)
|
||||||
|
DebugPrint() << v << " Count=" << count << ",ePtr=0x" << std::hex << ePtr;
|
||||||
if (!hashData || !ePtr)
|
if (!hashData || !ePtr)
|
||||||
return SymbolGroupValueVector();
|
return SymbolGroupValueVector();
|
||||||
// Retrieve the array of buckets of 'd'
|
// Retrieve the array of buckets of 'd'
|
||||||
@@ -762,7 +773,7 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
|
|||||||
if (!bucketPointers)
|
if (!bucketPointers)
|
||||||
return SymbolGroupValueVector();
|
return SymbolGroupValueVector();
|
||||||
// Get list of buckets (starting elements of 'QHashData::Node')
|
// 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 ?
|
const SymbolGroupValueVector buckets = SymbolGroupValue::pointerSize() == 8 ?
|
||||||
hashBuckets(v.node()->symbolGroup(), dummyNodeType,
|
hashBuckets(v.node()->symbolGroup(), dummyNodeType,
|
||||||
reinterpret_cast<const ULONG64 *>(bucketPointers), numBuckets,
|
reinterpret_cast<const ULONG64 *>(bucketPointers), numBuckets,
|
||||||
@@ -786,13 +797,18 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
|
|||||||
dummyNodeList.push_back(l);
|
dummyNodeList.push_back(l);
|
||||||
if (dummyNodeList.size() >= count) // Stop at maximum count
|
if (dummyNodeList.size() >= count) // Stop at maximum count
|
||||||
notEnough = false;
|
notEnough = false;
|
||||||
|
if (debugMap)
|
||||||
|
DebugPrint() << '#' << (dummyNodeList.size() - 1) << "l=" << l << ",next=" << next;
|
||||||
|
l = next;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Finally convert them into real nodes 'QHashNode<K,V> (potentially expensive)
|
// 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;
|
SymbolGroupValueVector nodeList;
|
||||||
nodeList.reserve(count);
|
nodeList.reserve(count);
|
||||||
const SymbolGroupValueVector::const_iterator dcend = dummyNodeList.end();
|
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'.
|
// QSet<>: Contains a 'QHash<key, QHashDummyValue>' as member 'q_hash'.
|
||||||
// Just dump the keys as an array.
|
// Just dump the keys as an array.
|
||||||
static inline AbstractSymbolGroupNodePtrVector
|
static inline AbstractSymbolGroupNodePtrVector
|
||||||
qSetChildList(const SymbolGroupValue &v, VectorIndexType count)
|
qSetChildList(const SymbolGroupValue &v, int count)
|
||||||
{
|
{
|
||||||
const SymbolGroupValue qHash = v["q_hash"];
|
const SymbolGroupValue qHash = v["q_hash"];
|
||||||
AbstractSymbolGroupNodePtrVector rc;
|
AbstractSymbolGroupNodePtrVector rc;
|
||||||
@@ -831,7 +847,7 @@ static inline AbstractSymbolGroupNodePtrVector
|
|||||||
|
|
||||||
// QHash<>: Add with fake map nodes.
|
// QHash<>: Add with fake map nodes.
|
||||||
static inline AbstractSymbolGroupNodePtrVector
|
static inline AbstractSymbolGroupNodePtrVector
|
||||||
qHashChildList(const SymbolGroupValue &v, VectorIndexType count)
|
qHashChildList(const SymbolGroupValue &v, int count)
|
||||||
{
|
{
|
||||||
AbstractSymbolGroupNodePtrVector rc;
|
AbstractSymbolGroupNodePtrVector rc;
|
||||||
if (!count)
|
if (!count)
|
||||||
@@ -852,6 +868,106 @@ static inline AbstractSymbolGroupNodePtrVector
|
|||||||
return rc;
|
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,
|
AbstractSymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int type,
|
||||||
int size, const SymbolGroupValueContext &ctx)
|
int size, const SymbolGroupValueContext &ctx)
|
||||||
{
|
{
|
||||||
@@ -884,6 +1000,12 @@ AbstractSymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int ty
|
|||||||
break;
|
break;
|
||||||
case KT_QSet:
|
case KT_QSet:
|
||||||
return qSetChildList(SymbolGroupValue(node, ctx), size);
|
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:
|
case KT_QStringList:
|
||||||
if (const SymbolGroupValue qList = SymbolGroupValue(node, ctx)[unsigned(0)])
|
if (const SymbolGroupValue qList = SymbolGroupValue(node, ctx)[unsigned(0)])
|
||||||
return qListChildList(qList, size);
|
return qListChildList(qList, size);
|
||||||
|
|||||||
@@ -109,9 +109,10 @@ static const CommandDescription commandDescriptions[] = {
|
|||||||
"-c complex dumpers"},
|
"-c complex dumpers"},
|
||||||
{"locals",
|
{"locals",
|
||||||
"Prints local variables of symbol group in GDBMI or debug format",
|
"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"
|
"-h human-readable ouput\n"
|
||||||
"-d debug output\n"
|
"-d debug output\n"
|
||||||
|
"-f debug_filter\n"
|
||||||
"-c complex dumpers\n"
|
"-c complex dumpers\n"
|
||||||
"-e expand-list Comma-separated list of inames to be expanded beforehand\n"
|
"-e expand-list Comma-separated list of inames to be expanded beforehand\n"
|
||||||
"-u uninitialized-list Comma-separated list of uninitialized inames\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 ?
|
const unsigned succeeded = runComplexDumpers ?
|
||||||
symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces()), &errorMessage) :
|
symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &errorMessage) :
|
||||||
symGroup->expandList(inames, &errorMessage);
|
symGroup->expandList(inames, &errorMessage);
|
||||||
|
|
||||||
ExtensionContext::instance().report('R', token, "expandlocals", "%u/%u %s",
|
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
|
// Parse the command
|
||||||
unsigned debugOutput = 0;
|
unsigned debugOutput = 0;
|
||||||
std::string iname;
|
std::string iname;
|
||||||
|
std::string debugFilter;
|
||||||
StringList tokens = commandTokens<StringList>(args, token);
|
StringList tokens = commandTokens<StringList>(args, token);
|
||||||
StringVector expandedInames;
|
StringVector expandedInames;
|
||||||
StringVector uninitializedInames;
|
StringVector uninitializedInames;
|
||||||
@@ -324,6 +326,14 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
|
|||||||
split(tokens.front(), ',', std::back_inserter(uninitializedInames));
|
split(tokens.front(), ',', std::back_inserter(uninitializedInames));
|
||||||
tokens.pop_front();
|
tokens.pop_front();
|
||||||
break;
|
break;
|
||||||
|
case 'f':
|
||||||
|
if (tokens.empty()) {
|
||||||
|
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
debugFilter = tokens.front();
|
||||||
|
tokens.pop_front();
|
||||||
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
if (tokens.empty()) {
|
if (tokens.empty()) {
|
||||||
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
|
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
|
||||||
@@ -362,7 +372,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
|
|||||||
if (!tokens.empty())
|
if (!tokens.empty())
|
||||||
iname = tokens.front();
|
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);
|
SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage);
|
||||||
if (!symGroup)
|
if (!symGroup)
|
||||||
return std::string();
|
return std::string();
|
||||||
@@ -379,7 +389,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (debugOutput)
|
if (debugOutput)
|
||||||
return symGroup->debug(iname, debugOutput - 1);
|
return symGroup->debug(iname, debugFilter, debugOutput - 1);
|
||||||
|
|
||||||
return iname.empty() ?
|
return iname.empty() ?
|
||||||
symGroup->dump(dumpContext, parameters) :
|
symGroup->dump(dumpContext, parameters) :
|
||||||
@@ -430,7 +440,7 @@ static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int
|
|||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
std::wstring value;
|
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;
|
*errorMessage = "Cannot dump " + iname;
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
void trimFront(std::string &s);
|
void trimFront(std::string &s);
|
||||||
void trimBack(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.
|
// Format numbers, etc, as a string.
|
||||||
template <class Streamable>
|
template <class Streamable>
|
||||||
std::string toString(const Streamable s)
|
std::string toString(const Streamable s)
|
||||||
|
|||||||
@@ -33,10 +33,12 @@
|
|||||||
|
|
||||||
#include "symbolgroup.h"
|
#include "symbolgroup.h"
|
||||||
#include "stringutils.h"
|
#include "stringutils.h"
|
||||||
|
#include "gdbmihelpers.h"
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
typedef std::vector<int>::size_type VectorIndexType;
|
typedef std::vector<int>::size_type VectorIndexType;
|
||||||
typedef std::vector<std::string> StringVector;
|
typedef std::vector<std::string> StringVector;
|
||||||
@@ -48,13 +50,21 @@ const char rootNameC[] = "local";
|
|||||||
SymbolGroup::SymbolGroup(IDebugSymbolGroup2 *sg,
|
SymbolGroup::SymbolGroup(IDebugSymbolGroup2 *sg,
|
||||||
const SymbolParameterVector &vec,
|
const SymbolParameterVector &vec,
|
||||||
ULONG threadId,
|
ULONG threadId,
|
||||||
unsigned frame) :
|
unsigned frame,
|
||||||
|
const std::string &function) :
|
||||||
m_symbolGroup(sg),
|
m_symbolGroup(sg),
|
||||||
m_threadId(threadId),
|
m_threadId(threadId),
|
||||||
m_frame(frame),
|
m_frame(frame),
|
||||||
m_root(0)
|
m_root(0),
|
||||||
|
m_function(function)
|
||||||
{
|
{
|
||||||
m_root = SymbolGroupNode::create(this, rootNameC, vec);
|
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()
|
SymbolGroup::~SymbolGroup()
|
||||||
@@ -135,6 +145,7 @@ SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugS
|
|||||||
IDebugSymbolGroup2 *idebugSymbols = 0;
|
IDebugSymbolGroup2 *idebugSymbols = 0;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
SymbolParameterVector parameters;
|
SymbolParameterVector parameters;
|
||||||
|
std::string func;
|
||||||
|
|
||||||
// Obtain symbol group at stack frame.
|
// Obtain symbol group at stack frame.
|
||||||
do {
|
do {
|
||||||
@@ -149,6 +160,11 @@ SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugS
|
|||||||
*errorMessage = str.str();
|
*errorMessage = str.str();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
StackFrame frameData;
|
||||||
|
if (!getFrame(frame, &frameData, errorMessage))
|
||||||
|
break;
|
||||||
|
func = wStringToString(frameData.function);
|
||||||
|
|
||||||
hr = debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &idebugSymbols);
|
hr = debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &idebugSymbols);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgDebugEngineComFailed("GetScopeSymbolGroup2", hr);
|
*errorMessage = msgDebugEngineComFailed("GetScopeSymbolGroup2", hr);
|
||||||
@@ -176,7 +192,7 @@ SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugS
|
|||||||
idebugSymbols->Release();
|
idebugSymbols->Release();
|
||||||
return 0;
|
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)
|
static inline std::string msgNotFound(const std::string &nodeName)
|
||||||
@@ -246,16 +262,21 @@ std::string SymbolGroup::dump(const std::string &iname,
|
|||||||
return str.str();
|
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;
|
std::ostringstream str;
|
||||||
str << '\n';
|
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()) {
|
if (iname.empty()) {
|
||||||
accept(visitor);
|
accept(*visitor);
|
||||||
} else {
|
} else {
|
||||||
if (AbstractSymbolGroupNode *const node = find(iname)) {
|
if (AbstractSymbolGroupNode *const node = find(iname)) {
|
||||||
node->accept(visitor, SymbolGroupNodeVisitor::parentIname(iname), 0, 0);
|
node->accept(*visitor, SymbolGroupNodeVisitor::parentIname(iname), 0, 0);
|
||||||
} else {
|
} else {
|
||||||
str << msgNotFound(iname);
|
str << msgNotFound(iname);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ private:
|
|||||||
|
|
||||||
explicit SymbolGroup(CIDebugSymbolGroup *,
|
explicit SymbolGroup(CIDebugSymbolGroup *,
|
||||||
const SymbolParameterVector &vec,
|
const SymbolParameterVector &vec,
|
||||||
ULONG threadId, unsigned frame);
|
ULONG threadId, unsigned frame,
|
||||||
|
const std::string &function);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector;
|
typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector;
|
||||||
@@ -73,9 +74,13 @@ public:
|
|||||||
// Expand node and dump
|
// Expand node and dump
|
||||||
std::string dump(const std::string &iname, const SymbolGroupValueContext &ctx,
|
std::string dump(const std::string &iname, const SymbolGroupValueContext &ctx,
|
||||||
const DumpParameters &p, std::string *errorMessage);
|
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; }
|
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; }
|
ULONG threadId() const { return m_threadId; }
|
||||||
SymbolGroupNode *root() { return m_root; }
|
SymbolGroupNode *root() { return m_root; }
|
||||||
const SymbolGroupNode *root() const { return m_root; }
|
const SymbolGroupNode *root() const { return m_root; }
|
||||||
@@ -127,6 +132,8 @@ private:
|
|||||||
const unsigned m_frame;
|
const unsigned m_frame;
|
||||||
const ULONG m_threadId;
|
const ULONG m_threadId;
|
||||||
SymbolGroupNode *m_root;
|
SymbolGroupNode *m_root;
|
||||||
|
std::string m_function;
|
||||||
|
std::string m_module;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SYMBOLGROUP_H
|
#endif // SYMBOLGROUP_H
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#include "stringutils.h"
|
#include "stringutils.h"
|
||||||
#include "base64.h"
|
#include "base64.h"
|
||||||
#include "containers.h"
|
#include "containers.h"
|
||||||
|
#include "extensioncontext.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@@ -913,6 +914,15 @@ static inline std::string msgCannotCast(const std::string &nodeName,
|
|||||||
return str.str();
|
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!
|
// Expand!
|
||||||
bool SymbolGroupNode::expand(std::string *errorMessage)
|
bool SymbolGroupNode::expand(std::string *errorMessage)
|
||||||
{
|
{
|
||||||
@@ -926,18 +936,21 @@ bool SymbolGroupNode::expand(std::string *errorMessage)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!canExpand()) {
|
if (!canExpand()) {
|
||||||
*errorMessage = "No subelements to expand in node: " + absoluteFullIName();
|
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index,
|
||||||
|
"No subelements to expand in node.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (flags() & Uninitialized) {
|
if (flags() & Uninitialized) {
|
||||||
*errorMessage = "Refusing to expand uninitialized node: " + absoluteFullIName();
|
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index,
|
||||||
|
"Refusing to expand uninitialized node.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HRESULT hr = m_symbolGroup->debugSymbolGroup()->ExpandSymbol(m_index, TRUE);
|
const HRESULT hr = m_symbolGroup->debugSymbolGroup()->ExpandSymbol(m_index, TRUE);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
SymbolGroup::SymbolParameterVector parameters;
|
SymbolGroup::SymbolParameterVector parameters;
|
||||||
@@ -945,8 +958,10 @@ bool SymbolGroupNode::expand(std::string *errorMessage)
|
|||||||
// and corrected SubElement count (might be estimate))
|
// and corrected SubElement count (might be estimate))
|
||||||
if (!SymbolGroup::getSymbolParameters(m_symbolGroup->debugSymbolGroup(),
|
if (!SymbolGroup::getSymbolParameters(m_symbolGroup->debugSymbolGroup(),
|
||||||
m_index, m_parameters.SubElements + 1,
|
m_index, m_parameters.SubElements + 1,
|
||||||
¶meters, errorMessage))
|
¶meters, errorMessage)) {
|
||||||
|
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, *errorMessage);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
// Before inserting children, correct indexes on whole group
|
// Before inserting children, correct indexes on whole group
|
||||||
m_symbolGroup->root()->notifyExpanded(m_index + 1, parameters.at(0).SubElements);
|
m_symbolGroup->root()->notifyExpanded(m_index + 1, parameters.at(0).SubElements);
|
||||||
// Parse parameters, correct our own) and create child nodes.
|
// 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);
|
HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("AddSymbol", 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;
|
return 0;
|
||||||
}
|
}
|
||||||
SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS());
|
SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS());
|
||||||
hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(index, 1, &(*parameters.begin()));
|
hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(index, 1, &(*parameters.begin()));
|
||||||
if (FAILED(hr)) { // Should never fail
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
// Paranoia: Check for cuckoo's eggs (which should not happen)
|
// Paranoia: Check for cuckoo's eggs (which should not happen)
|
||||||
@@ -1137,6 +1161,24 @@ SymbolGroupNodeVisitor::VisitResult
|
|||||||
return VisitContinue;
|
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::DumpSymbolGroupNodeVisitor(std::ostream &os,
|
DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os,
|
||||||
const SymbolGroupValueContext &context,
|
const SymbolGroupValueContext &context,
|
||||||
@@ -1158,9 +1200,10 @@ SymbolGroupNodeVisitor::VisitResult
|
|||||||
// Recurse to children only if expanded by explicit watchmodel request
|
// Recurse to children only if expanded by explicit watchmodel request
|
||||||
// and initialized.
|
// and initialized.
|
||||||
m_visitChildren = true;
|
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())
|
if (SymbolGroupNode *snode = node->asSymbolGroupNode())
|
||||||
m_visitChildren = snode->isExpanded()
|
m_visitChildren = depth < 1 && snode->isExpanded()
|
||||||
&& (flags & (SymbolGroupNode::Uninitialized|SymbolGroupNode::ExpandedByDumper)) == 0;
|
&& (flags & (SymbolGroupNode::Uninitialized|SymbolGroupNode::ExpandedByDumper)) == 0;
|
||||||
// Comma between same level children given obscured children
|
// Comma between same level children given obscured children
|
||||||
if (depth == m_lastDepth) {
|
if (depth == m_lastDepth) {
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ protected:
|
|||||||
VisitStop
|
VisitStop
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
virtual VisitResult visit(AbstractSymbolGroupNode *node,
|
virtual VisitResult visit(AbstractSymbolGroupNode *node,
|
||||||
const std::string &fullIname,
|
const std::string &fullIname,
|
||||||
unsigned child, unsigned depth) = 0;
|
unsigned child, unsigned depth) = 0;
|
||||||
@@ -365,15 +365,32 @@ class DebugSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
|
|||||||
public:
|
public:
|
||||||
explicit DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity = 0);
|
explicit DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity = 0);
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
virtual VisitResult visit(AbstractSymbolGroupNode *node,
|
virtual VisitResult visit(AbstractSymbolGroupNode *node,
|
||||||
const std::string &fullIname,
|
const std::string &fullIname,
|
||||||
unsigned child, unsigned depth);
|
unsigned child, unsigned depth);
|
||||||
|
|
||||||
|
private:
|
||||||
std::ostream &m_os;
|
std::ostream &m_os;
|
||||||
const unsigned m_verbosity;
|
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.
|
// GDBMI dump output visitor.
|
||||||
class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
|
class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
|
||||||
public:
|
public:
|
||||||
@@ -381,12 +398,13 @@ public:
|
|||||||
const SymbolGroupValueContext &context,
|
const SymbolGroupValueContext &context,
|
||||||
const DumpParameters ¶meters = DumpParameters());
|
const DumpParameters ¶meters = DumpParameters());
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
virtual VisitResult visit(AbstractSymbolGroupNode *node,
|
virtual VisitResult visit(AbstractSymbolGroupNode *node,
|
||||||
const std::string &fullIname,
|
const std::string &fullIname,
|
||||||
unsigned child, unsigned depth);
|
unsigned child, unsigned depth);
|
||||||
virtual void childrenVisited(const AbstractSymbolGroupNode * node, unsigned depth);
|
virtual void childrenVisited(const AbstractSymbolGroupNode * node, unsigned depth);
|
||||||
|
|
||||||
|
private:
|
||||||
std::ostream &m_os;
|
std::ostream &m_os;
|
||||||
const SymbolGroupValueContext &m_context;
|
const SymbolGroupValueContext &m_context;
|
||||||
const DumpParameters &m_parameters;
|
const DumpParameters &m_parameters;
|
||||||
|
|||||||
@@ -37,6 +37,14 @@
|
|||||||
#include "containers.h"
|
#include "containers.h"
|
||||||
|
|
||||||
#include <iomanip>
|
#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,
|
SymbolGroupValue::SymbolGroupValue(SymbolGroupNode *node,
|
||||||
const SymbolGroupValueContext &ctx) :
|
const SymbolGroupValueContext &ctx) :
|
||||||
@@ -62,7 +70,7 @@ SymbolGroupValue SymbolGroupValue::operator[](unsigned index) const
|
|||||||
if (index < m_node->children().size())
|
if (index < m_node->children().size())
|
||||||
if (SymbolGroupNode *n = m_node->childAt(index)->asSymbolGroupNode())
|
if (SymbolGroupNode *n = m_node->childAt(index)->asSymbolGroupNode())
|
||||||
return SymbolGroupValue(n, m_context);
|
return SymbolGroupValue(n, m_context);
|
||||||
return SymbolGroupValue();
|
return SymbolGroupValue(m_errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SymbolGroupValue::ensureExpanded() const
|
bool SymbolGroupValue::ensureExpanded() const
|
||||||
@@ -88,7 +96,7 @@ SymbolGroupValue SymbolGroupValue::operator[](const char *name) const
|
|||||||
if (AbstractSymbolGroupNode *child = m_node->childByIName(name))
|
if (AbstractSymbolGroupNode *child = m_node->childByIName(name))
|
||||||
if (SymbolGroupNode *n = child->asSymbolGroupNode())
|
if (SymbolGroupNode *n = child->asSymbolGroupNode())
|
||||||
return SymbolGroupValue(n, m_context);
|
return SymbolGroupValue(n, m_context);
|
||||||
return SymbolGroupValue();
|
return SymbolGroupValue(m_errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SymbolGroupValue::type() const
|
std::string SymbolGroupValue::type() const
|
||||||
@@ -253,6 +261,12 @@ unsigned SymbolGroupValue::pointerSize()
|
|||||||
return ps;
|
return ps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned SymbolGroupValue::intSize()
|
||||||
|
{
|
||||||
|
static const unsigned is = SymbolGroupValue::sizeOf("int");
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
std::string SymbolGroupValue::stripPointerType(const std::string &t)
|
std::string SymbolGroupValue::stripPointerType(const std::string &t)
|
||||||
{
|
{
|
||||||
return isPointerType(t) ? t.substr(0, t.size() - 2) : 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;
|
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"
|
// get the inner types: "QMap<int, double>" -> "int", "double"
|
||||||
std::vector<std::string> SymbolGroupValue::innerTypesOf(const std::string &t)
|
std::vector<std::string> SymbolGroupValue::innerTypesOf(const std::string &t)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,16 +39,18 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
class SymbolGroupNode;
|
class SymbolGroupNode;
|
||||||
|
|
||||||
// Structure to pass all IDebug interfaces used for SymbolGroupValue
|
// Structure to pass all IDebug interfaces used for SymbolGroupValue
|
||||||
struct SymbolGroupValueContext
|
struct SymbolGroupValueContext
|
||||||
{
|
{
|
||||||
SymbolGroupValueContext(CIDebugDataSpaces *ds) : dataspaces(ds) {}
|
SymbolGroupValueContext(CIDebugDataSpaces *ds, CIDebugSymbols *s) : dataspaces(ds), symbols(s) {}
|
||||||
SymbolGroupValueContext::SymbolGroupValueContext() : dataspaces(0) {}
|
SymbolGroupValueContext::SymbolGroupValueContext() : dataspaces(0), symbols(0) {}
|
||||||
|
|
||||||
CIDebugDataSpaces *dataspaces;
|
CIDebugDataSpaces *dataspaces;
|
||||||
|
CIDebugSymbols *symbols;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* SymbolGroupValue: Flyweight tied to a SymbolGroupNode
|
/* SymbolGroupValue: Flyweight tied to a SymbolGroupNode
|
||||||
@@ -57,6 +59,8 @@ struct SymbolGroupValueContext
|
|||||||
|
|
||||||
class SymbolGroupValue
|
class SymbolGroupValue
|
||||||
{
|
{
|
||||||
|
explicit SymbolGroupValue(const std::string &parentError);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SymbolGroupValue(SymbolGroupNode *node, const SymbolGroupValueContext &c);
|
explicit SymbolGroupValue(SymbolGroupNode *node, const SymbolGroupValueContext &c);
|
||||||
SymbolGroupValue();
|
SymbolGroupValue();
|
||||||
@@ -98,7 +102,11 @@ public:
|
|||||||
static std::string addPointerType(const std::string &);
|
static std::string addPointerType(const std::string &);
|
||||||
static std::string stripArrayType(const std::string &);
|
static std::string stripArrayType(const std::string &);
|
||||||
static bool isPointerType(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 pointerSize();
|
||||||
|
static unsigned intSize();
|
||||||
|
|
||||||
// get the inner types: "QMap<int, double>" -> "int", "double"
|
// get the inner types: "QMap<int, double>" -> "int", "double"
|
||||||
static std::vector<std::string> innerTypesOf(const std::string &t);
|
static std::vector<std::string> innerTypesOf(const std::string &t);
|
||||||
@@ -115,6 +123,25 @@ private:
|
|||||||
// For debugging purposes
|
// For debugging purposes
|
||||||
std::ostream &operator<<(std::ostream &, const SymbolGroupValue &v);
|
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
|
/* Helpers for detecting types reported from IDebugSymbolGroup
|
||||||
* 1) Class prefix==true is applicable to outer types obtained from
|
* 1) Class prefix==true is applicable to outer types obtained from
|
||||||
* from IDebugSymbolGroup: 'class foo' or 'struct foo'.
|
* from IDebugSymbolGroup: 'class foo' or 'struct foo'.
|
||||||
|
|||||||
Reference in New Issue
Block a user